PostProcessingWriter.cpp

00001 /*
00002 
00003 Copyright (c) 2005-2015, 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 #include "Hdf5DataWriter.hpp"
00046 #include "Hdf5ToMeshalyzerConverter.hpp"
00047 #include "Hdf5ToVtkConverter.hpp"
00048 
00049 #include <iostream>
00050 
00051 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00052 PostProcessingWriter<ELEMENT_DIM, SPACE_DIM>::PostProcessingWriter(AbstractTetrahedralMesh<ELEMENT_DIM,SPACE_DIM>& rMesh,
00053                                                                    const FileFinder& rDirectory,
00054                                                                    std::string hdf5File,
00055                                                                    std::string voltageName)
00056         : mDirectory(rDirectory),
00057           mHdf5File(hdf5File),
00058           mVoltageName(voltageName),
00059           mrMesh(rMesh)
00060 {
00061     mLo = mrMesh.GetDistributedVectorFactory()->GetLow();
00062     mHi = mrMesh.GetDistributedVectorFactory()->GetHigh();
00063     mpDataReader = new Hdf5DataReader(mDirectory, mHdf5File);
00064     mpCalculator = new PropagationPropertiesCalculator(mpDataReader, voltageName);
00065     // Check that the hdf file was generated by simulations from (probably) the same mesh.
00066     assert(mpDataReader->GetNumberOfRows() == mrMesh.GetNumNodes());
00067 }
00068 
00069 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00070 void PostProcessingWriter<ELEMENT_DIM, SPACE_DIM>::WritePostProcessingFiles()
00071 {
00072     //Check that post-processing is really needed
00073     assert(HeartConfig::Instance()->IsPostProcessingRequested());
00074 
00075     // Please note that only the master processor should write to file.
00076     // Each of the private methods called here takes care of checking.
00077     if (HeartConfig::Instance()->IsApdMapsRequested())
00078     {
00079         std::vector<std::pair<double,double> > apd_maps;
00080         HeartConfig::Instance()->GetApdMaps(apd_maps);
00081         for (unsigned i=0; i<apd_maps.size(); i++)
00082         {
00083             WriteApdMapFile(apd_maps[i].first, apd_maps[i].second);
00084         }
00085     }
00086 
00087     if (HeartConfig::Instance()->IsUpstrokeTimeMapsRequested())
00088     {
00089         std::vector<double> upstroke_time_maps;
00090         HeartConfig::Instance()->GetUpstrokeTimeMaps(upstroke_time_maps);
00091         for (unsigned i=0; i<upstroke_time_maps.size(); i++)
00092         {
00093             WriteUpstrokeTimeMap(upstroke_time_maps[i]);
00094         }
00095     }
00096 
00097     if (HeartConfig::Instance()->IsMaxUpstrokeVelocityMapRequested())
00098     {
00099         std::vector<double> upstroke_velocity_maps;
00100         HeartConfig::Instance()->GetMaxUpstrokeVelocityMaps(upstroke_velocity_maps);
00101         for (unsigned i=0; i<upstroke_velocity_maps.size(); i++)
00102         {
00103             WriteMaxUpstrokeVelocityMap(upstroke_velocity_maps[i]);
00104         }
00105     }
00106 
00107     if (HeartConfig::Instance()->IsConductionVelocityMapsRequested())
00108     {
00109         std::vector<unsigned> conduction_velocity_maps;
00110         HeartConfig::Instance()->GetConductionVelocityMaps(conduction_velocity_maps);
00111 
00112         //get the mesh here
00113         DistanceMapCalculator<ELEMENT_DIM, SPACE_DIM> dist_map_calculator(mrMesh);
00114 
00115         for (unsigned i=0; i<conduction_velocity_maps.size(); i++)
00116         {
00117             std::vector<double> distance_map;
00118             std::vector<unsigned> origin_surface;
00119             origin_surface.push_back(conduction_velocity_maps[i]);
00120             dist_map_calculator.ComputeDistanceMap(origin_surface, distance_map);
00121             WriteConductionVelocityMap(conduction_velocity_maps[i], distance_map);
00122         }
00123     }
00124 
00125     if (HeartConfig::Instance()->IsAnyNodalTimeTraceRequested())
00126     {
00127         std::vector<unsigned> requested_nodes;
00128         HeartConfig::Instance()->GetNodalTimeTraceRequested(requested_nodes);
00129         WriteVariablesOverTimeAtNodes(requested_nodes);
00130     }
00131 
00132     if (HeartConfig::Instance()->IsPseudoEcgCalculationRequested())
00133     {
00134         std::vector<ChastePoint<SPACE_DIM> > electrodes;
00135         HeartConfig::Instance()->GetPseudoEcgElectrodePositions(electrodes);
00136 
00139         delete mpDataReader;
00140 
00141         for (unsigned i=0; i<electrodes.size(); i++)
00142         {
00143             PseudoEcgCalculator<ELEMENT_DIM,SPACE_DIM,1> calculator(mrMesh,
00144                                                                     electrodes[i],
00145                                                                     mDirectory,
00146                                                                     mHdf5File,
00147                                                                     mVoltageName);
00148             calculator.WritePseudoEcg();
00149         }
00150 
00152         mpDataReader = new Hdf5DataReader(mDirectory, mHdf5File);
00153         mpCalculator->SetHdf5DataReader(mpDataReader);
00154     }
00155 }
00156 
00157 
00158 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00159 PostProcessingWriter<ELEMENT_DIM, SPACE_DIM>::~PostProcessingWriter()
00160 {
00161     delete mpDataReader;
00162     delete mpCalculator;
00163 }
00164 
00165 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00166 void PostProcessingWriter<ELEMENT_DIM, SPACE_DIM>::WriteOutputDataToHdf5(const std::vector<std::vector<double> >& rDataPayload,
00167                                                                          const std::string& rDatasetName,
00168                                                                          const std::string& rDatasetUnit,
00169                                                                          const std::string& rUnlimitedVariableName,
00170                                                                          const std::string& rUnlimitedVariableUnit)
00171 {
00172     DistributedVectorFactory* p_factory = mrMesh.GetDistributedVectorFactory();
00173     FileFinder test_output("", RelativeTo::ChasteTestOutput);
00174     Hdf5DataWriter writer(*p_factory,
00175                           mDirectory.GetRelativePath(test_output),  // Path relative to CHASTE_TEST_OUTPUT
00176                           mHdf5File,
00177                           false, // to wiping
00178                           true,  // to extending
00179                           rDatasetName); // dataset name
00180 
00181     int apd_id = writer.DefineVariable(rDatasetName,rDatasetUnit);
00182     writer.DefineFixedDimension(mrMesh.GetNumNodes());
00183     writer.DefineUnlimitedDimension(rUnlimitedVariableName, rUnlimitedVariableUnit);
00184     writer.EndDefineMode();
00185 
00186     //Determine the maximum number of paces
00187     unsigned local_max_paces = 0u;
00188     for (unsigned node_index = 0; node_index < rDataPayload.size(); ++node_index)
00189     {
00190         if (rDataPayload[node_index].size() > local_max_paces)
00191         {
00192              local_max_paces = rDataPayload[node_index].size();
00193         }
00194     }
00195 
00196     unsigned max_paces = 0u;
00197     MPI_Allreduce(&local_max_paces, &max_paces, 1, MPI_UNSIGNED, MPI_MAX, PETSC_COMM_WORLD);
00198 
00199     for (unsigned pace_idx = 0; pace_idx < max_paces; pace_idx++)
00200     {
00201         Vec apd_vec = p_factory->CreateVec();
00202         DistributedVector distributed_vector = p_factory->CreateDistributedVector(apd_vec);
00203         for (DistributedVector::Iterator index = distributed_vector.Begin();
00204              index!= distributed_vector.End();
00205              ++index)
00206         {
00207             unsigned node_idx = index.Local;
00208             // pad with -999 if no pace defined at this node
00209             if (pace_idx < rDataPayload[node_idx].size() )
00210             {
00211                 distributed_vector[index] = rDataPayload[node_idx][pace_idx];
00212             }
00213             else
00214             {
00215                 distributed_vector[index] = -999.0;
00216             }
00217         }
00218         writer.PutVector(apd_id, apd_vec);
00219         PetscTools::Destroy(apd_vec);
00220         writer.PutUnlimitedVariable(pace_idx);
00221         writer.AdvanceAlongUnlimitedDimension();
00222     }
00223     writer.Close();
00224 }
00225 
00226 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00227 void PostProcessingWriter<ELEMENT_DIM, SPACE_DIM>::WriteApdMapFile(double repolarisationPercentage, double threshold)
00228 {
00229     std::vector<std::vector<double> > local_output_data = mpCalculator->CalculateAllActionPotentialDurationsForNodeRange(repolarisationPercentage, mLo, mHi, threshold);
00230 
00231     // HDF5 shouldn't have minus signs in the data names..
00232     std::stringstream hdf5_dataset_name;
00233     hdf5_dataset_name << "Apd_" << repolarisationPercentage;
00234 
00235     WriteOutputDataToHdf5(local_output_data,
00236                           hdf5_dataset_name.str() + ConvertToHdf5FriendlyString(threshold) + "_Map",
00237                           "msec");
00238 }
00239 
00240 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00241 void PostProcessingWriter<ELEMENT_DIM, SPACE_DIM>::WriteUpstrokeTimeMap(double threshold)
00242 {
00243     std::vector<std::vector<double> > output_data;
00244     //Fill in data
00245     for (unsigned node_index = mLo; node_index < mHi; node_index++)
00246     {
00247         std::vector<double> upstroke_times;
00248         try
00249         {
00250             upstroke_times = mpCalculator->CalculateUpstrokeTimes(node_index, threshold);
00251             assert(upstroke_times.size() != 0);
00252         }
00253         catch(Exception&)
00254         {
00255             upstroke_times.push_back(0);
00256             assert(upstroke_times.size() == 1);
00257         }
00258         output_data.push_back(upstroke_times);
00259     }
00260 
00261     WriteOutputDataToHdf5(output_data,
00262                           "UpstrokeTimeMap" + ConvertToHdf5FriendlyString(threshold),
00263                           "msec");
00264 }
00265 
00266 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00267 void PostProcessingWriter<ELEMENT_DIM, SPACE_DIM>::WriteMaxUpstrokeVelocityMap(double threshold)
00268 {
00269     std::vector<std::vector<double> > output_data;
00270     //Fill in data
00271     for (unsigned node_index = mLo; node_index < mHi; node_index++)
00272     {
00273         std::vector<double> upstroke_velocities;
00274         try
00275         {
00276             upstroke_velocities = mpCalculator->CalculateAllMaximumUpstrokeVelocities(node_index, threshold);
00277             assert(upstroke_velocities.size() != 0);
00278         }
00279         catch(Exception&)
00280         {
00281             upstroke_velocities.push_back(0);
00282             assert(upstroke_velocities.size() ==1);
00283         }
00284         output_data.push_back(upstroke_velocities);
00285     }
00286 
00287     WriteOutputDataToHdf5(output_data,
00288                           "MaxUpstrokeVelocityMap" + ConvertToHdf5FriendlyString(threshold),
00289                           "mV_per_msec");
00290 }
00291 
00292 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00293 void PostProcessingWriter<ELEMENT_DIM, SPACE_DIM>::WriteConductionVelocityMap(unsigned originNode, std::vector<double> distancesFromOriginNode)
00294 {
00295     //Fill in data
00296     std::vector<std::vector<double> > output_data;
00297     for (unsigned dest_node = mLo; dest_node < mHi; dest_node++)
00298     {
00299         std::vector<double> conduction_velocities;
00300         try
00301         {
00302             conduction_velocities = mpCalculator->CalculateAllConductionVelocities(originNode, dest_node, distancesFromOriginNode[dest_node]);
00303             assert(conduction_velocities.size() != 0);
00304         }
00305         catch(Exception&)
00306         {
00307             conduction_velocities.push_back(0);
00308             assert(conduction_velocities.size() == 1);
00309         }
00310         output_data.push_back(conduction_velocities);
00311     }
00312     std::stringstream filename_stream;
00313     filename_stream << "ConductionVelocityFromNode" << originNode;
00314 
00315     WriteOutputDataToHdf5(output_data, filename_stream.str(), "cm_per_msec");
00316 }
00317 
00318 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00319 void PostProcessingWriter<ELEMENT_DIM, SPACE_DIM>::WriteAboveThresholdDepolarisationFile(double threshold )
00320 {
00321     std::vector<std::vector<double> > output_data;
00322 
00323     //Fill in data
00324     for (unsigned node_index = mLo; node_index < mHi; node_index++)
00325     {
00326         std::vector<double> upstroke_velocities;
00327         std::vector<unsigned> above_threshold_depolarisations;
00328         std::vector<double> output_item;
00329         bool no_upstroke_occurred = false;
00330 
00331         try
00332         {
00333             upstroke_velocities = mpCalculator->CalculateAllMaximumUpstrokeVelocities(node_index, threshold);
00334             assert(upstroke_velocities.size() != 0);
00335         }
00336         catch(Exception&)
00337         {
00338             upstroke_velocities.push_back(0);
00339             assert(upstroke_velocities.size() == 1);
00340             no_upstroke_occurred = true;
00341         }
00342         // This method won't throw any exception, so there is no need to put it into the try/catch:
00343         above_threshold_depolarisations =  mpCalculator->CalculateAllAboveThresholdDepolarisations(node_index, threshold);
00344 
00345         // Count the total above threshold depolarisations
00346         unsigned total_number_of_above_threshold_depolarisations = 0;
00347         for (unsigned ead_index = 0; ead_index< above_threshold_depolarisations.size();ead_index++)
00348         {
00349             total_number_of_above_threshold_depolarisations = total_number_of_above_threshold_depolarisations + above_threshold_depolarisations[ead_index];
00350         }
00351 
00352         // For this item, push back the number of upstrokes...
00353         if (no_upstroke_occurred)
00354         {
00355             output_item.push_back(0);
00356         }
00357         else
00358         {
00359             output_item.push_back((double)upstroke_velocities.size());
00360         }
00361         //... and the number of above threshold depolarisations
00362         output_item.push_back((double) total_number_of_above_threshold_depolarisations);
00363 
00364         output_data.push_back(output_item);
00365     }
00366 
00367     // we just use meshalyzer format so that something is generated in simple column format for use with gnuplot etc.
00368     WriteGenericFileToMeshalyzer(output_data, "", "AboveThresholdDepolarisations" + ConvertToHdf5FriendlyString(threshold) +
00369                                  ".dat");
00370 }
00371 
00372 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00373 std::string PostProcessingWriter<ELEMENT_DIM, SPACE_DIM>::ConvertToHdf5FriendlyString(double threshold)
00374 {
00375     std::stringstream dataset_stream;
00376     std::string extra_message = "_";
00377     if (threshold < 0.0)
00378     {
00379         extra_message += "minus_";
00380         threshold = -threshold;
00381     }
00382 
00383     // if threshold is a decimal eg: because using phenomenological model
00384     if (threshold - floor(threshold) > 1e-8)
00385     {
00386         // give the answer to 2dp
00387         dataset_stream << extra_message << floor(threshold) << "pt" << floor(threshold*100)-(floor(threshold)*100);
00388     }
00389     else
00390     {
00391         dataset_stream << extra_message << threshold;
00392     }
00393 
00394     return dataset_stream.str();
00395 }
00396 
00397 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00398 void PostProcessingWriter<ELEMENT_DIM, SPACE_DIM>::WriteVariablesOverTimeAtNodes(std::vector<unsigned>& rNodeIndices)
00399 {
00400     std::vector<std::string> variable_names = mpDataReader->GetVariableNames();
00401 
00402     //we will write one file per variable in the hdf5 file
00403     for (unsigned name_index=0; name_index < variable_names.size(); name_index++)
00404     {
00405         std::vector<std::vector<double> > output_data;
00406         if (PetscTools::AmMaster())//only master process fills the data structure
00407         {
00408             //allocate memory: NXM matrix where N = number of time steps and M number of requested nodes
00409             output_data.resize( mpDataReader->GetUnlimitedDimensionValues().size() );
00410             for (unsigned j = 0; j < mpDataReader->GetUnlimitedDimensionValues().size(); j++)
00411             {
00412                 output_data[j].resize(rNodeIndices.size());
00413             }
00414 
00415             for (unsigned requested_index = 0; requested_index < rNodeIndices.size(); requested_index++)
00416             {
00417                 unsigned node_index = rNodeIndices[requested_index];
00418 
00419                 //handle permutation, if any
00420                 if ( (mrMesh.rGetNodePermutation().size() != 0) &&
00421                       !HeartConfig::Instance()->GetOutputUsingOriginalNodeOrdering() )
00422                 {
00423                     node_index = mrMesh.rGetNodePermutation()[ rNodeIndices[requested_index] ];
00424                 }
00425 
00426                 //grab the data from the hdf5 file.
00427                 std::vector<double> time_series = mpDataReader->GetVariableOverTime(variable_names[name_index], node_index);
00428                 assert ( time_series.size() == mpDataReader->GetUnlimitedDimensionValues().size());
00429 
00430                 //fill the output_data data structure
00431                 for (unsigned time_step = 0; time_step < time_series.size(); time_step++)
00432                 {
00433                     output_data[time_step][requested_index] = time_series[time_step];
00434                 }
00435             }
00436         }
00437         std::stringstream filename_stream;
00438         filename_stream << "NodalTraces_" << variable_names[name_index] << ".dat";
00439         // we just use meshalyzer format so that something is generated in simple column format for use with gnuplot etc.
00440         WriteGenericFileToMeshalyzer(output_data, "", filename_stream.str());
00441     }
00442 }
00443 
00444 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00445 void PostProcessingWriter<ELEMENT_DIM, SPACE_DIM>::WriteGenericFileToMeshalyzer(std::vector<std::vector<double> >& rDataPayload, const std::string& rFolder, const std::string& rFileName)
00446 {
00447     OutputFileHandler output_file_handler(HeartConfig::Instance()->GetOutputDirectory() + "/" + rFolder, false);
00448     PetscTools::BeginRoundRobin();
00449     {
00450         out_stream p_file=out_stream(NULL);
00451         //Open file
00452         if (PetscTools::AmMaster())
00453         {
00454             // Open the file for the first time
00455             p_file = output_file_handler.OpenOutputFile(rFileName);
00456         }
00457         else
00458         {
00459             // Append data to the existing file opened by master
00460             p_file = output_file_handler.OpenOutputFile(rFileName, std::ios::app);
00461         }
00462         // Write data
00463         for (unsigned line_number=0; line_number<rDataPayload.size(); line_number++)
00464         {
00465             for (unsigned i = 0; i < rDataPayload[line_number].size(); i++)
00466             {
00467                 *p_file << rDataPayload[line_number][i] << "\t";
00468             }
00469             *p_file << std::endl;
00470         }
00471 
00472         // Last processor appends comment line
00473         if (PetscTools::AmTopMost())
00474         {
00475             std::string comment = "# " + ChasteBuildInfo::GetProvenanceString();
00476             *p_file << comment;
00477         }
00478         p_file->close();
00479     }
00480     //There's a barrier included here: Process i+1 waits for process i to close the file
00481     PetscTools::EndRoundRobin();
00482 }
00483 
00484 
00486 // Explicit instantiation
00488 
00489 template class PostProcessingWriter<1,1>;
00490 template class PostProcessingWriter<1,2>;
00491 template class PostProcessingWriter<2,2>;
00492 template class PostProcessingWriter<1,3>;
00493 template class PostProcessingWriter<3,3>;

Generated by  doxygen 1.6.2