Chaste Release::3.1
PostProcessingWriter.cpp
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 #include "UblasCustomFunctions.hpp"
00037 #include "HeartConfig.hpp"
00038 #include "PostProcessingWriter.hpp"
00039 #include "PetscTools.hpp"
00040 #include "OutputFileHandler.hpp"
00041 #include "DistanceMapCalculator.hpp"
00042 #include "PseudoEcgCalculator.hpp"
00043 #include "Version.hpp"
00044 #include "HeartEventHandler.hpp"
00045 
00046 #include <iostream>
00047 
00048 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00049 PostProcessingWriter<ELEMENT_DIM, SPACE_DIM>::PostProcessingWriter(AbstractTetrahedralMesh<ELEMENT_DIM,SPACE_DIM>& rMesh,
00050                                                                    std::string directory,
00051                                                                    std::string hdf5File,
00052                                                                    bool makeAbsolute,
00053                                                                    std::string voltageName)
00054         : mDirectory(directory),
00055           mHdf5File(hdf5File),
00056           mMakeAbsolute(makeAbsolute),
00057           mVoltageName(voltageName),
00058           mrMesh(rMesh)
00059 {
00060     mLo = mrMesh.GetDistributedVectorFactory()->GetLow();
00061     mHi = mrMesh.GetDistributedVectorFactory()->GetHigh();
00062     mpDataReader = new Hdf5DataReader(directory, hdf5File, makeAbsolute);
00063     mpCalculator = new PropagationPropertiesCalculator(mpDataReader,voltageName);
00064     //check that the hdf file was generated by simulations from the same mesh
00065     assert(mpDataReader->GetNumberOfRows() == mrMesh.GetNumNodes());
00066 }
00067 
00068 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00069 void PostProcessingWriter<ELEMENT_DIM, SPACE_DIM>::WritePostProcessingFiles()
00070 {
00071     //Check that post-processing is really needed
00072     assert(HeartConfig::Instance()->IsPostProcessingRequested());
00073     //Check that it's safe to send the results to the (hard-coded) subfolder for Meshalyzer/CMGui
00074     assert(HeartConfig::Instance()->GetVisualizeWithMeshalyzer() || HeartConfig::Instance()->GetVisualizeWithCmgui());
00075 
00076     // Please note that only the master processor should write to file.
00077     // Each of the private methods called here takes care of checking.
00078 
00079     if (HeartConfig::Instance()->IsApdMapsRequested())
00080     {
00081         std::vector<std::pair<double,double> > apd_maps;
00082         HeartConfig::Instance()->GetApdMaps(apd_maps);
00083         for (unsigned i=0; i<apd_maps.size(); i++)
00084         {
00085             WriteApdMapFile(apd_maps[i].first, apd_maps[i].second);
00086         }
00087     }
00088 
00089     if (HeartConfig::Instance()->IsUpstrokeTimeMapsRequested())
00090     {
00091         std::vector<double> upstroke_time_maps;
00092         HeartConfig::Instance()->GetUpstrokeTimeMaps(upstroke_time_maps);
00093         for (unsigned i=0; i<upstroke_time_maps.size(); i++)
00094         {
00095             WriteUpstrokeTimeMap(upstroke_time_maps[i]);
00096         }
00097     }
00098 
00099     if (HeartConfig::Instance()->IsMaxUpstrokeVelocityMapRequested())
00100     {
00101         std::vector<double> upstroke_velocity_maps;
00102         HeartConfig::Instance()->GetMaxUpstrokeVelocityMaps(upstroke_velocity_maps);
00103         for (unsigned i=0; i<upstroke_velocity_maps.size(); i++)
00104         {
00105             WriteMaxUpstrokeVelocityMap(upstroke_velocity_maps[i]);
00106         }
00107     }
00108 
00109     if (HeartConfig::Instance()->IsConductionVelocityMapsRequested())
00110     {
00111         std::vector<unsigned> conduction_velocity_maps;
00112         HeartConfig::Instance()->GetConductionVelocityMaps(conduction_velocity_maps);
00113 
00114         //get the mesh here
00115         DistanceMapCalculator<ELEMENT_DIM, SPACE_DIM> dist_map_calculator(mrMesh);
00116 
00117         for (unsigned i=0; i<conduction_velocity_maps.size(); i++)
00118         {
00119             std::vector<double> distance_map;
00120             std::vector<unsigned> origin_surface;
00121             origin_surface.push_back(conduction_velocity_maps[i]);
00122             dist_map_calculator.ComputeDistanceMap(origin_surface, distance_map);
00123             WriteConductionVelocityMap(conduction_velocity_maps[i], distance_map);
00124         }
00125     }
00126 
00127     if (HeartConfig::Instance()->IsAnyNodalTimeTraceRequested())
00128     {
00129         std::vector<unsigned> requested_nodes;
00130         HeartConfig::Instance()->GetNodalTimeTraceRequested(requested_nodes);
00131         WriteVariablesOverTimeAtNodes(requested_nodes);
00132     }
00133 
00134     if (HeartConfig::Instance()->IsPseudoEcgCalculationRequested())
00135     {
00136         std::vector<ChastePoint<SPACE_DIM> > electrodes;
00137         HeartConfig::Instance()->GetPseudoEcgElectrodePositions(electrodes);
00138 
00139         for (unsigned i=0; i<electrodes.size(); i++)
00140         {
00141             PseudoEcgCalculator<ELEMENT_DIM,SPACE_DIM,1> calculator(mrMesh, electrodes[i], mDirectory, mHdf5File, mVoltageName, mMakeAbsolute);
00142             calculator.WritePseudoEcg();
00143         }
00144     }
00145 }
00146 
00147 
00148 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00149 PostProcessingWriter<ELEMENT_DIM, SPACE_DIM>::~PostProcessingWriter()
00150 {
00151     delete mpDataReader;
00152     delete mpCalculator;
00153 }
00154 
00155 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00156 void PostProcessingWriter<ELEMENT_DIM, SPACE_DIM>::WriteApdMapFile(double repolarisationPercentage, double threshold)
00157 {
00158     std::vector<std::vector<double> > output_data = mpCalculator->CalculateAllActionPotentialDurationsForNodeRange(repolarisationPercentage, mLo, mHi, threshold);
00159     std::stringstream filename_stream;
00160     filename_stream << "Apd_" << repolarisationPercentage << "_" << threshold << "_Map.dat";
00161     WriteGenericFile(output_data, filename_stream.str());
00162 }
00163 
00164 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00165 void PostProcessingWriter<ELEMENT_DIM, SPACE_DIM>::WriteUpstrokeTimeMap(double threshold)
00166 {
00167     std::vector<std::vector<double> > output_data;
00168     //Fill in data
00169     for (unsigned node_index = mLo; node_index < mHi; node_index++)
00170     {
00171         std::vector<double> upstroke_times;
00172         try
00173         {
00174             upstroke_times = mpCalculator->CalculateUpstrokeTimes(node_index, threshold);
00175             assert(upstroke_times.size() != 0);
00176         }
00177         catch(Exception& e)
00178         {
00179             upstroke_times.push_back(0);
00180             assert(upstroke_times.size() == 1);
00181         }
00182         output_data.push_back(upstroke_times);
00183     }
00184     std::stringstream filename_stream;
00185     filename_stream << "UpstrokeTimeMap_" << threshold << ".dat";
00186     WriteGenericFile(output_data, filename_stream.str());
00187 }
00188 
00189 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00190 void PostProcessingWriter<ELEMENT_DIM, SPACE_DIM>::WriteMaxUpstrokeVelocityMap(double threshold)
00191 {
00192     std::vector<std::vector<double> > output_data;
00193     //Fill in data
00194     for (unsigned node_index = mLo; node_index < mHi; node_index++)
00195     {
00196         std::vector<double> upstroke_velocities;
00197         try
00198         {
00199             upstroke_velocities = mpCalculator->CalculateAllMaximumUpstrokeVelocities(node_index, threshold);
00200             assert(upstroke_velocities.size() != 0);
00201         }
00202         catch(Exception& e)
00203         {
00204             upstroke_velocities.push_back(0);
00205             assert(upstroke_velocities.size() ==1);
00206         }
00207         output_data.push_back(upstroke_velocities);
00208     }
00209     std::stringstream filename_stream;
00210     filename_stream << "MaxUpstrokeVelocityMap_" << threshold << ".dat";
00211     WriteGenericFile(output_data, filename_stream.str());
00212 }
00213 
00214 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00215 void PostProcessingWriter<ELEMENT_DIM, SPACE_DIM>::WriteConductionVelocityMap(unsigned originNode, std::vector<double> distancesFromOriginNode)
00216 {
00217     //Fill in data
00218     std::vector<std::vector<double> > output_data;
00219     for (unsigned dest_node = mLo; dest_node < mHi; dest_node++)
00220     {
00221         std::vector<double> conduction_velocities;
00222         try
00223         {
00224             conduction_velocities = mpCalculator->CalculateAllConductionVelocities(originNode, dest_node, distancesFromOriginNode[dest_node]);
00225             assert(conduction_velocities.size() != 0);
00226         }
00227         catch(Exception& e)
00228         {
00229             conduction_velocities.push_back(0);
00230             assert(conduction_velocities.size() == 1);
00231         }
00232         output_data.push_back(conduction_velocities);
00233     }
00234     std::stringstream filename_stream;
00235     filename_stream << "ConductionVelocityFromNode" << originNode << ".dat";
00236     WriteGenericFile(output_data, filename_stream.str());
00237 }
00238 
00239 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00240 void PostProcessingWriter<ELEMENT_DIM, SPACE_DIM>::WriteAboveThresholdDepolarisationFile(double threshold )
00241 {
00242     std::vector<std::vector<double> > output_data;
00243 
00244     //Fill in data
00245     for (unsigned node_index = mLo; node_index < mHi; node_index++)
00246     {
00247         std::vector<double> upstroke_velocities;
00248         std::vector<unsigned> above_threshold_depolarisations;
00249         std::vector<double> output_item;
00250         bool no_upstroke_occurred = false;
00251 
00252         try
00253         {
00254             upstroke_velocities = mpCalculator->CalculateAllMaximumUpstrokeVelocities(node_index, threshold);
00255             assert(upstroke_velocities.size() != 0);
00256         }
00257         catch(Exception& e)
00258         {
00259             upstroke_velocities.push_back(0);
00260             assert(upstroke_velocities.size() ==1);
00261             no_upstroke_occurred = true;
00262         }
00263         //this method won't throw any exception, so there is no need to put it into the try/catch
00264         above_threshold_depolarisations =  mpCalculator->CalculateAllAboveThresholdDepolarisations(node_index, threshold);
00265 
00266         //count the total above threshold depolarisations
00267         unsigned total_number_of_above_threshold_depolarisations = 0;
00268         for (unsigned ead_index = 0; ead_index< above_threshold_depolarisations.size();ead_index++)
00269         {
00270             total_number_of_above_threshold_depolarisations = total_number_of_above_threshold_depolarisations + above_threshold_depolarisations[ead_index];
00271         }
00272 
00273         //for this item, push back the number of upstrokes...
00274         if (no_upstroke_occurred)
00275         {
00276             output_item.push_back(0);
00277         }
00278         else
00279         {
00280             output_item.push_back(upstroke_velocities.size());
00281         }
00282         //... and the number of above threshold depolarisations
00283         output_item.push_back((double) total_number_of_above_threshold_depolarisations);
00284 
00285         output_data.push_back(output_item);
00286     }
00287     std::stringstream filename_stream;
00288     filename_stream << "AboveThresholdDepolarisations" << threshold << ".dat";
00289     WriteGenericFile(output_data, filename_stream.str());
00290 }
00291 
00292 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00293 void PostProcessingWriter<ELEMENT_DIM, SPACE_DIM>::WriteVariablesOverTimeAtNodes(std::vector<unsigned>& rNodeIndices)
00294 {
00295     std::vector<std::string> variable_names = mpDataReader->GetVariableNames();
00296 
00297     //we will write one file per variable in the hdf5 file
00298     for (unsigned name_index=0; name_index < variable_names.size(); name_index++)
00299     {
00300         std::vector<std::vector<double> > output_data;
00301         if (PetscTools::AmMaster())//only master process fills the data structure
00302         {
00303             //allocate memory: NXM matrix where N = number of time steps and M number of requested nodes
00304             output_data.resize( mpDataReader->GetUnlimitedDimensionValues().size() );
00305             for (unsigned j = 0; j < mpDataReader->GetUnlimitedDimensionValues().size(); j++)
00306             {
00307                 output_data[j].resize(rNodeIndices.size());
00308             }
00309 
00310             for (unsigned requested_index = 0; requested_index < rNodeIndices.size(); requested_index++)
00311             {
00312                 unsigned node_index = rNodeIndices[requested_index];
00313 
00314                 //handle permutation, if any
00315                 if ( (mrMesh.rGetNodePermutation().size() != 0) &&
00316                       !HeartConfig::Instance()->GetOutputUsingOriginalNodeOrdering() )
00317                 {
00318                     node_index = mrMesh.rGetNodePermutation()[ rNodeIndices[requested_index] ];
00319                 }
00320 
00321                 //grab the data from the hdf5 file.
00322                 std::vector<double> time_series =  mpDataReader->GetVariableOverTime(variable_names[name_index], node_index);
00323                 assert ( time_series.size() == mpDataReader->GetUnlimitedDimensionValues().size());
00324 
00325                 //fill the output_data data structure
00326                 for (unsigned time_step = 0; time_step < time_series.size(); time_step++)
00327                 {
00328                     output_data[time_step][requested_index] = time_series[time_step];
00329                 }
00330             }
00331         }
00332         std::stringstream filename_stream;
00333         filename_stream << "NodalTraces_" << variable_names[name_index] << ".dat";
00334         WriteGenericFile(output_data, filename_stream.str());
00335     }
00336 }
00337 
00338 
00339 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00340 void PostProcessingWriter<ELEMENT_DIM, SPACE_DIM>::WriteGenericFile(std::vector<std::vector<double> >& rDataPayload, const std::string& rFileName)
00341 {
00342 
00343     if(HeartConfig::Instance()->GetVisualizeWithMeshalyzer())
00344     {
00345         WriteGenericFileToMeshalyzer(rDataPayload, "output", rFileName);
00346     }
00347     if(HeartConfig::Instance()->GetVisualizeWithCmgui())
00348     {
00349         //Special case use of the wrong method - \todo #1660 need to change the format of this data
00350         WriteGenericFileToMeshalyzer(rDataPayload, "cmgui_output", rFileName);
00351     }
00352 
00353 
00354 }
00355 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00356 void PostProcessingWriter<ELEMENT_DIM, SPACE_DIM>::WriteGenericFileToMeshalyzer(std::vector<std::vector<double> >& rDataPayload, const std::string& rFolder, const std::string& rFileName)
00357 {
00358     OutputFileHandler output_file_handler(HeartConfig::Instance()->GetOutputDirectory() + "/" + rFolder, false);
00359     PetscTools::BeginRoundRobin();
00360     {
00361         out_stream p_file=out_stream(NULL);
00362         //Open file
00363         if (PetscTools::AmMaster())
00364         {
00365             //Open the file for the first time
00366             p_file = output_file_handler.OpenOutputFile(rFileName);
00367             //write provenance info
00368             std::string comment = "# " + ChasteBuildInfo::GetProvenanceString();
00369             *p_file << comment;
00370         }
00371         else
00372         {
00373             //Append to the existing file
00374             p_file = output_file_handler.OpenOutputFile(rFileName, std::ios::app);
00375         }
00376         //Write data
00377         for (unsigned line_number=0; line_number<rDataPayload.size(); line_number++)
00378         {
00379             for (unsigned i = 0; i < rDataPayload[line_number].size(); i++)
00380             {
00381                 *p_file << rDataPayload[line_number][i] << "\t";
00382             }
00383             *p_file << std::endl;
00384         }
00385         p_file->close();
00386     }
00387     //There's a barrier included here: Process i+1 waits for process i to close the file
00388     PetscTools::EndRoundRobin();
00389 }
00390 
00392 // Explicit instantiation
00394 
00395 template class PostProcessingWriter<1,1>;
00396 template class PostProcessingWriter<1,2>;
00397 template class PostProcessingWriter<2,2>;
00398 template class PostProcessingWriter<1,3>;
00399 template class PostProcessingWriter<3,3>;