Chaste Release::3.1
RunAndCheckIonicModels.hpp
00001 /*
00002 
00003 Copyright (c) 2005-2012, 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 
00037 #ifndef _RUNANDCHECKIONICMODELS_HPP_
00038 #define _RUNANDCHECKIONICMODELS_HPP_
00039 
00040 #include <vector>
00041 #include <string>
00042 
00043 #include "OdeSolution.hpp"
00044 
00045 #include "ColumnDataWriter.hpp"
00046 #include "ColumnDataReader.hpp"
00047 
00048 #include "AbstractCardiacCell.hpp"
00049 #include "HeartConfig.hpp"
00050 
00051 void RunOdeSolverWithIonicModel(AbstractCardiacCellInterface* pOdeSystem,
00052                                 double endTime,
00053                                 std::string filename,
00054                                 int stepPerRow=100,
00055                                 bool doComputeExceptVoltage=true,
00056                                 bool useSamplingInterval=false);
00057 
00058 void CheckCellModelResults(const std::string& rBaseResultsFilename,
00059                            std::string validResultsBasename = "",
00060                            double tolerance = 1e-3);
00061 
00062 std::vector<double> GetVoltages(ColumnDataReader& rReader);
00063 
00064 void CompareCellModelResults(std::string baseResultsFilename1, std::string baseResultsFilename2,
00065                              double tolerance, bool vOnly=false, std::string folderName="TestIonicModels");
00066 
00067 
00068 /*
00069  * Note: we have to have the function definitions here, rather than in a .cpp file, if we're
00070  * to include them in heart/src, as otherwise linking of the heart library fails because
00071  * CxxTest routines are not defined.
00072  */
00073 
00074 #include <cmath>
00075 
00076 void RunOdeSolverWithIonicModel(AbstractCardiacCellInterface* pOdeSystem,
00077                                 double endTime,
00078                                 std::string filename,
00079                                 int stepPerRow,
00080                                 bool doComputeExceptVoltage,
00081                                 bool useSamplingInterval)
00082 {
00083     double start_time = 0.0;
00084 
00085     if (doComputeExceptVoltage)
00086     {
00087         // Store the current system state
00088         std::vector<double> state_variables_copy = pOdeSystem->GetStdVecStateVariables();
00089 
00090         // Test ComputeExceptVoltage
00091         double v_init = pOdeSystem->GetVoltage();
00092         pOdeSystem->ComputeExceptVoltage(start_time, start_time + 100.0 /*ms*/);
00093         double v_end = pOdeSystem->GetVoltage();
00094         TS_ASSERT_DELTA(v_init, v_end, 1e-6);
00095 
00096         // Test SetVoltage
00097         pOdeSystem->SetVoltage(1e6);
00098         TS_ASSERT_DELTA(pOdeSystem->GetVoltage(), 1e6, 1e-6);
00099 
00100         // Reset the system
00101         pOdeSystem->SetStateVariables(state_variables_copy);
00102     }
00103 
00104     // Solve and write to file
00105     if (useSamplingInterval)
00106     {
00107         OdeSolution solution = pOdeSystem->Compute(start_time, endTime, HeartConfig::Instance()->GetOdeTimeStep() * stepPerRow);
00108         solution.WriteToFile("TestIonicModels", filename, "ms", 1, false, 4);
00109     }
00110     else
00111     {
00112         OdeSolution solution = pOdeSystem->Compute(start_time, endTime);
00113         solution.WriteToFile("TestIonicModels", filename, "ms", stepPerRow, false, 4);
00114     }
00115 }
00116 
00117 std::vector<double> GetVoltages(ColumnDataReader& rReader)
00118 {
00119     //Rather Ugly, we can't currently guarantee what the name of the voltage column is,
00120     //hence we try to cover the most common possibilities
00121     std::vector<double> voltages;
00122     if (rReader.HasValues("V"))
00123     {
00124         voltages = rReader.GetValues("V");
00125     }
00126     else if (rReader.HasValues("membrane_voltage"))
00127     {
00128         voltages = rReader.GetValues("membrane_voltage");
00129     }
00130     else if (rReader.HasValues("membrane__V"))
00131     {
00132         voltages = rReader.GetValues("membrane__V");
00133     }
00134     else
00135     {
00136         EXCEPTION("Model membrane voltage not recognised.");
00137     }
00138     return voltages;
00139 }
00140 
00141 std::vector<double> GetIntracellularCalcium(ColumnDataReader& rReader)
00142 {
00143     //Rather Ugly, we can't currently guarantee what the name of the calcium column is,
00144     //hence we try to cover the most common possibilities
00145     std::vector<double> cai;
00146     if (rReader.HasValues("CaI"))
00147     {
00148         cai = rReader.GetValues("CaI");
00149     }
00150     else if (rReader.HasValues("cytosolic_calcium_concentration"))
00151     {
00152         cai = rReader.GetValues("cytosolic_calcium_concentration");
00153     }
00154     else if (rReader.HasValues("Cai"))
00155     {
00156         cai = rReader.GetValues("Cai");
00157     }
00158     else
00159     {
00160         EXCEPTION("Model intracellular calcium is not recognised.");
00161     }
00162     return cai;
00163 }
00164 
00165 std::vector<double> GetHGate(ColumnDataReader& rReader)
00166 {
00167     //Rather Ugly, we can't currently guarantee what the name of the h gate column is,
00168     //hence we try to cover the most common possibilities
00169     std::vector<double> h_values;
00170     if (rReader.HasValues("h"))
00171     {
00172         h_values = rReader.GetValues("h");
00173     }
00174     else if (rReader.HasValues("fast_sodium_current_h_gate__h"))
00175     {
00176         h_values = rReader.GetValues("fast_sodium_current_h_gate__h");
00177     }
00178     else if (rReader.HasValues("membrane_fast_sodium_current_h_gate"))
00179     {
00180         h_values = rReader.GetValues("membrane_fast_sodium_current_h_gate");
00181     }
00182     else
00183     {
00184         EXCEPTION("Model h gate is not recognised.");
00185     }
00186     return h_values;
00187 }
00188 
00189 /*
00190  * Check the cell model against a previous version
00191  * or another source e.g. Alan's COR
00192  */
00193 void CheckCellModelResults(const std::string& rBaseResultsFilename,
00194                            std::string validResultsBasename,
00195                            double tolerance)
00196 {
00197     // read data entries for the new file
00198     ColumnDataReader data_reader("TestIonicModels", rBaseResultsFilename);
00199     std::vector<double> times = data_reader.GetValues("Time");
00200     std::vector<double> voltages = GetVoltages(data_reader);
00201 
00202     if (validResultsBasename == "")
00203     {
00204         validResultsBasename = rBaseResultsFilename;
00205     }
00206 
00207     ColumnDataReader valid_reader("heart/test/data/ionicmodels", validResultsBasename + "ValidData",
00208                                   false);
00209     std::vector<double> valid_times = valid_reader.GetValues("Time");
00210     std::vector<double> valid_voltages = GetVoltages(valid_reader);
00211 
00212     TS_ASSERT_EQUALS(times.size(), valid_times.size());
00213     for (unsigned i=0; i<valid_times.size(); i++)
00214     {
00215         TS_ASSERT_DELTA(times[i], valid_times[i], 1e-12);
00216         TS_ASSERT_DELTA(voltages[i], valid_voltages[i], tolerance);
00217     }
00218 }
00219 
00220 void CompareCellModelResults(std::string baseResultsFilename1, std::string baseResultsFilename2,
00221                              double tolerance, bool vOnly, std::string folderName)
00222 {
00223     // Compare 2 sets of results, e.g. from 2 different solvers for the same model.
00224     // If the time series differ, the finer resolution must be given first.
00225     ColumnDataReader data_reader1(folderName, baseResultsFilename1);
00226     std::vector<double> times1 = data_reader1.GetValues("Time");
00227     std::vector<double> voltages1 = GetVoltages(data_reader1);
00228     std::vector<double> calcium1;
00229     std::vector<double> h1;
00230 
00231     ColumnDataReader data_reader2(folderName, baseResultsFilename2);
00232     std::vector<double> times2 = data_reader2.GetValues("Time");
00233     std::vector<double> voltages2 = GetVoltages(data_reader2);
00234     std::vector<double> calcium2;
00235     std::vector<double> h2;
00236 
00237     if (!vOnly)
00238     {
00239         calcium1 = GetIntracellularCalcium(data_reader1);
00240         h1 = GetHGate(data_reader1);
00241         calcium2 = GetIntracellularCalcium(data_reader2);
00242         h2 = GetHGate(data_reader2);
00243     }
00244 
00245     TS_ASSERT(times1.size() >= times2.size());
00246     double last_v = voltages2[0];
00247     double tol = tolerance;
00248     for (unsigned i=0, j=0; i<times2.size(); i++)
00249     {
00250         // Find corresponding time index
00251         while (j<times1.size() && times1[j] < times2[i] - 1e-12)
00252         {
00253             j++;
00254         }
00255 
00256         // Set tolerance higher in upstroke
00257         if (fabs(voltages2[i] - last_v) > 0.05)
00258         {
00259             tol = tolerance * 25;
00260         }
00261         else
00262         {
00263             tol = tolerance;
00264         }
00265         last_v = voltages2[i];
00266 
00267         TS_ASSERT_DELTA(times1[j], times2[i], 1e-12);
00268         // adjust tol to data
00269         TS_ASSERT_DELTA(voltages1[j], voltages2[i], tol);
00270         if (!vOnly)
00271         {
00272             TS_ASSERT_DELTA(calcium1[j],  calcium2[i],  tol/100);
00273             TS_ASSERT_DELTA(h1[j],        h2[i],        tol/10);
00274         }
00275     }
00276 }
00277 
00278 
00279 #endif //_RUNANDCHECKIONICMODELS_HPP_