Chaste  Release::3.4
CellProperties.cpp
1 /*
2 
3 Copyright (c) 2005-2016, University of Oxford.
4 All rights reserved.
5 
6 University of Oxford means the Chancellor, Masters and Scholars of the
7 University of Oxford, having an administrative office at Wellington
8 Square, Oxford OX1 2JD, UK.
9 
10 This file is part of Chaste.
11 
12 Redistribution and use in source and binary forms, with or without
13 modification, are permitted provided that the following conditions are met:
14  * Redistributions of source code must retain the above copyright notice,
15  this list of conditions and the following disclaimer.
16  * Redistributions in binary form must reproduce the above copyright notice,
17  this list of conditions and the following disclaimer in the documentation
18  and/or other materials provided with the distribution.
19  * Neither the name of the University of Oxford nor the names of its
20  contributors may be used to endorse or promote products derived from this
21  software without specific prior written permission.
22 
23 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
27 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
29 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32 OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 
34 */
35 
36 #include <cmath>
37 #include <sstream>
38 #include <cassert>
39 
40 #include "CellProperties.hpp"
41 #include "Exception.hpp"
42 
43 
44 enum APPhases { BELOWTHRESHOLD , ABOVETHRESHOLD };
45 
47 {
48  // Check we have some suitable data to process
49  if (mrTime.size() < 1)
50  {
51  EXCEPTION("Insufficient time steps to calculate physiological properties.");
52  }
53 
54  if (mrTime.size() != mrVoltage.size())
55  {
56  EXCEPTION("Time and Voltage series should be the same length. Time.size() = " << mrTime.size() << ", Voltage.size() = " << mrVoltage.size());
57  }
58 
59 
60  double max_upstroke_velocity = -DBL_MAX;
61  double current_time_of_upstroke_velocity = 0;
62  double current_resting_value=DBL_MAX;
63  double current_peak=-DBL_MAX;
64  double current_minimum_velocity=DBL_MAX;
65  double prev_voltage_derivative=0;
66  unsigned ap_counter = 0;
67  unsigned counter_of_plateau_depolarisations = 0;
68  //boolean to keep track whether we are switching phase from BELOWTHRESHOLD to ABOVETHRESHOLD
69  bool switching_phase = false;
70  bool found_a_flat_bit=false;
71  APPhases ap_phase = BELOWTHRESHOLD;
72 
73  unsigned time_steps = mrTime.size()-1; //The number of time steps is the number of intervals
74 
75  double v = mrVoltage[0];
76  double t = mrTime[0];
77  double prev_v = v;
78  double prev_t = t;
79  double voltage_derivative;
80  const double resting_potential_gradient_threshold = 1e-2;
81 
82  for (unsigned i=1; i<=time_steps; i++)
83  {
84  v = mrVoltage[i];
85  t = mrTime[i];
86  voltage_derivative = (t == prev_t) ? 0.0 : (v - prev_v) / (t - prev_t);
87 
88  // Look for the max upstroke velocity and when it happens (could be below or above threshold).
89  if (voltage_derivative >= max_upstroke_velocity)
90  {
91  max_upstroke_velocity = voltage_derivative;
92  current_time_of_upstroke_velocity = t;
93 
94  }
95 
96  switch (ap_phase)
97  {
98  case BELOWTHRESHOLD:
99  //while below threshold, find the resting value by checking where the velocity is minimal
100  //i.e. when it is flattest. If we can't find a flat bit, instead go for the minimum voltage
101  //seen before the threshold.
102  if (fabs(voltage_derivative)<=current_minimum_velocity && fabs(voltage_derivative)<=resting_potential_gradient_threshold)
103  {
104  current_minimum_velocity=fabs(voltage_derivative);
105  current_resting_value = prev_v;
106  found_a_flat_bit=true;
107  }
108  else if(prev_v < current_resting_value && !found_a_flat_bit)
109  {
110  current_resting_value = prev_v;
111  }
112 
113  // If we cross the threshold, this counts as an AP
114  if ( v>mThreshold && prev_v <= mThreshold )
115  {
116  //register the resting value and re-initialise the minimum velocity
117  mRestingValues.push_back(current_resting_value);
118  current_minimum_velocity = DBL_MAX;
119  current_resting_value = DBL_MAX;
120 
121  //Register the onset time. Linear interpolation.
122  mOnsets.push_back(prev_t + (t-prev_t)/(v-prev_v)*(mThreshold-prev_v));
123 
124  //If it is not the first AP, calculate cycle length for the last two APs
125  if (ap_counter>0)
126  {
127  mCycleLengths.push_back( mOnsets[ap_counter]-mOnsets[ap_counter-1] );
128  }
129 
130  switching_phase = true;
131  found_a_flat_bit = false;
132  ap_phase = ABOVETHRESHOLD;
133  // no break here - deliberate fall through to next case
134  }
135  else
136  {
137  break;
138  }
139 
140  case ABOVETHRESHOLD:
141  //While above threshold, look for the peak potential for the current AP
142  if (v>current_peak)
143  {
144  current_peak = v;
145  }
146 
147  // we check whether we have above threshold depolarisations
148  // and only if if we haven't just switched from below threshold at this time step.
149  // The latter is to avoid recording things depending on resting behaviour (in case of sudden upstroke from rest)
150  if (prev_voltage_derivative<=0 && voltage_derivative>0 && !switching_phase)
151  {
152  counter_of_plateau_depolarisations++;
153  }
154 
155  // From the next time step, we are not "switching phase" any longer
156  // (we want to check for above threshold deolarisations)
157  switching_phase = false;
158 
159  // If we cross the threshold again, the AP is over
160  // and we register all the parameters.
161  if ( v<mThreshold && prev_v >= mThreshold )
162  {
163  //register peak value for this AP
164  mPeakValues.push_back(current_peak);
165  //Re-initialise the current_peak.
166  current_peak = mThreshold;
167 
168  //register maximum upstroke velocity for this AP
169  mMaxUpstrokeVelocities.push_back(max_upstroke_velocity);
170  //re-initialise max_upstroke_velocity
171  max_upstroke_velocity = -DBL_MAX;
172 
173  //register time when maximum upstroke velocity occurred for this AP
174  mTimesAtMaxUpstrokeVelocity.push_back(current_time_of_upstroke_velocity);
175  //re-initialise current_time_of_upstroke_velocity=t;
176  current_time_of_upstroke_velocity = 0.0;
177 
178  mCounterOfPlateauDepolarisations.push_back(counter_of_plateau_depolarisations);
179 
180  //update the counters.
181  ap_counter++;
182  ap_phase = BELOWTHRESHOLD;
183 
184  //reinitialise counter of plateau depolarisations
185  counter_of_plateau_depolarisations = 0;
186  }
187  break;
188  }
189  prev_v = v;
190  prev_t = t;
191  prev_voltage_derivative = voltage_derivative;
192  }
193 
194 
195  // One last check. If the simulation ends halfway through an AP
196  // i.e. if the vectors of onsets has more elements than the vectors
197  // of peak and upstroke properties (that are updated at the end of the AP),
198  // then we register the peak and upstroke values so far
199  // for the last incomplete AP.
200  if (mOnsets.size()>mMaxUpstrokeVelocities.size())
201  {
202  mMaxUpstrokeVelocities.push_back(max_upstroke_velocity);
203  mPeakValues.push_back(current_peak);
204  mTimesAtMaxUpstrokeVelocity.push_back(current_time_of_upstroke_velocity);
206  }
207 }
208 
209 
210 std::vector<double> CellProperties::CalculateActionPotentialDurations(const double percentage)
211 {
213 
214  double prev_v = mrVoltage[0];
215  unsigned APcounter=0;//will keep count of the APDs that we calculate
216  bool apd_is_calculated=true;//this will ensure we hit the target only once per AP.
217  std::vector<double> apds;
218  double target = DBL_MAX;
219  bool apd_starting_time_found=false;
220  double apd_start_time=DBL_MAX;
221 
222  double t;
223  double v;
224  for (unsigned i=1; i<mrTime.size(); i++)
225  {
226  t = mrTime[i];
227  v = mrVoltage[i];
228 
229  //First we make sure we stop calculating after the last AP has been calculated
230  if (APcounter<mPeakValues.size())
231  {
232  //Set the target potential
233  target = mRestingValues[APcounter]+0.01*(100-percentage)*(mPeakValues[APcounter]-mRestingValues[APcounter]);
234 
235  //if we reach the peak, we need to start to calculate an APD
236  if (fabs(v-mPeakValues[APcounter])<=1e-6)
237  {
238  apd_is_calculated = false;
239  }
240 
241  // Start the timing where we first cross the target voltage
242  if ( prev_v<v && prev_v<=target && v>=target && apd_starting_time_found==false)
243  {
244  // Linear interpolation of target crossing time.
245  apd_start_time=t+( (target-prev_v)/(v-prev_v) )*(t-mrTime[i-1]);
246  apd_starting_time_found = true;
247  }
248 
249  //if we hit the target while repolarising
250  //and we are told this apd is not calculated yet.
251  if ( prev_v>v && prev_v>=target && v<=target && apd_is_calculated==false)
252  {
253  // Linear interpolation of target crossing time.
254  apds.push_back (t - apd_start_time + ( (target-prev_v)/(v-prev_v) )*(t-mrTime[i-1]) );
255  APcounter++;
256  apd_is_calculated = true;
257  apd_starting_time_found = false;
258  }
259  }
260  prev_v = v;
261  }
262  if (apds.size() == 0)
263  {
264  EXCEPTION("No full action potential was recorded");
265  }
266  return apds;
267 }
268 
270 {
272  unsigned size = mPeakValues.size();
273  std::vector<double> amplitudes(size);
274  for (unsigned i=0; i< size ;i++)
275  {
276  amplitudes[i] = (mPeakValues[i] - mRestingValues[i]);
277  }
278  return amplitudes;
279 }
280 
282 {
283  return GetActionPotentialAmplitudes().back();
284 }
285 
287 {
288  // mOnsets and mRestingValues are all
289  // set at the same time, so checking one should suffice.
290  if (mOnsets.empty())
291  {
292  // possible false error here if the simulation started at time < 0
293  EXCEPTION("AP did not occur, never exceeded threshold voltage.");
294  }
295 }
296 
298 {
299  // mPeakValues, mMaxUpstrokeVelocities and mTimesAtMaxUpstrokeVelocity are all
300  // set at the same time so checking one should suffice.
301  if (mMaxUpstrokeVelocities.empty())
302  {
303  EXCEPTION("AP did not occur, never descended past threshold voltage.");
304  }
305 }
306 
307 //
308 // The Get std::vector<double> methods
309 //
310 
311 std::vector<double> CellProperties::GetCycleLengths()
312 {
313  return mCycleLengths;
314 }
315 
317 {
319  return mRestingValues;
320 }
321 
323 {
325  return mPeakValues;
326 }
327 
329 {
331  return mMaxUpstrokeVelocities;
332 }
333 
335 {
338 }
339 
340 std::vector<double> CellProperties::GetAllActionPotentialDurations(const double percentage)
341 {
342  return CalculateActionPotentialDurations(percentage);
343 }
344 
346 {
348 }
349 //
350 // The Get <double> methods
351 //
352 
354 {
356  return mPeakValues.back();
357 }
358 
360 {
362  double peak_value;
363  if (mUnfinishedActionPotentials && mPeakValues.size()>1u)
364  {
365  peak_value = mPeakValues[mPeakValues.size()-2u];
366  }
367  else if (mUnfinishedActionPotentials && mPeakValues.size()==1u)
368  {
369  EXCEPTION("No peak potential matching a full action potential was recorded.");
370  }
371  else
372  {
373  peak_value = mPeakValues.back();
374  }
375  return peak_value;
376 }
377 
379 {
381  return mMaxUpstrokeVelocities.back();
382 }
383 
385 {
387  double max_upstroke;
389  {
390  max_upstroke = mMaxUpstrokeVelocities[mMaxUpstrokeVelocities.size()-2u];
391  }
393  {
394  EXCEPTION("No MaxUpstrokeVelocity matching a full action potential was recorded.");
395  }
396  else
397  {
398  max_upstroke = mMaxUpstrokeVelocities.back();
399  }
400  return max_upstroke;
401 }
402 
404 {
406  return mTimesAtMaxUpstrokeVelocity.back();
407 }
408 
410 {
412  double max_upstroke_time;
414  {
415  max_upstroke_time = mTimesAtMaxUpstrokeVelocity[mTimesAtMaxUpstrokeVelocity.size()-2u];
416  }
418  {
419  EXCEPTION("No TimeAtMaxUpstrokeVelocity matching a full action potential was recorded.");
420  }
421  else
422  {
423  max_upstroke_time = mTimesAtMaxUpstrokeVelocity.back();
424  }
425  return max_upstroke_time;
426 }
427 
428 double CellProperties::GetLastActionPotentialDuration(const double percentage)
429 {
430  // We tested this returns a non-empty vector in the method.
431  return CalculateActionPotentialDurations(percentage).back();
432 }
433 
435 {
436  return mCounterOfPlateauDepolarisations.back();
437 }
438 
440 {
441  // We tested something sensible has happened in the vector returning method.
442  return GetRestingPotentials().back();
443 }
std::vector< double > GetAllActionPotentialDurations(const double percentage)
double GetLastMaxUpstrokeVelocity()
std::vector< double > mTimesAtMaxUpstrokeVelocity
double GetTimeAtLastCompleteMaxUpstrokeVelocity()
std::vector< double > GetRestingPotentials()
#define EXCEPTION(message)
Definition: Exception.hpp:143
std::vector< double > GetMaxUpstrokeVelocities()
const std::vector< double > & mrTime
double GetLastPeakPotential()
std::vector< double > GetActionPotentialAmplitudes()
std::vector< double > CalculateActionPotentialDurations(const double percentage)
unsigned GetNumberOfAboveThresholdDepolarisationsForLastAp()
std::vector< double > mCycleLengths
bool mUnfinishedActionPotentials
std::vector< double > GetTimesAtMaxUpstrokeVelocity()
double GetLastActionPotentialDuration(const double percentage)
double GetLastCompleteMaxUpstrokeVelocity()
double GetLastActionPotentialAmplitude()
void CheckExceededThreshold(void)
std::vector< double > mPeakValues
std::vector< double > mMaxUpstrokeVelocities
std::vector< unsigned > GetNumberOfAboveThresholdDepolarisationsForAllAps()
std::vector< double > mOnsets
std::vector< unsigned > mCounterOfPlateauDepolarisations
std::vector< double > GetCycleLengths()
double GetLastCompletePeakPotential()
double GetLastRestingPotential()
void CalculateProperties()
std::vector< double > mRestingValues
std::vector< double > GetPeakPotentials()
void CheckReturnedToThreshold(void)
const std::vector< double > & mrVoltage
double GetTimeAtLastMaxUpstrokeVelocity()