00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
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
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
00073 assert(HeartConfig::Instance()->IsPostProcessingRequested());
00074
00075
00076
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
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),
00176 mHdf5File,
00177 false,
00178 true,
00179 rDatasetName);
00180
00181 int apd_id = writer.DefineVariable(rDatasetName,rDatasetUnit);
00182 writer.DefineFixedDimension(mrMesh.GetNumNodes());
00183 writer.DefineUnlimitedDimension(rUnlimitedVariableName, rUnlimitedVariableUnit);
00184 writer.EndDefineMode();
00185
00186
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
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
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
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
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
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
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
00343 above_threshold_depolarisations = mpCalculator->CalculateAllAboveThresholdDepolarisations(node_index, threshold);
00344
00345
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
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
00362 output_item.push_back((double) total_number_of_above_threshold_depolarisations);
00363
00364 output_data.push_back(output_item);
00365 }
00366
00367
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
00384 if (threshold - floor(threshold) > 1e-8)
00385 {
00386
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
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())
00407 {
00408
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
00420 if ( (mrMesh.rGetNodePermutation().size() != 0) &&
00421 !HeartConfig::Instance()->GetOutputUsingOriginalNodeOrdering() )
00422 {
00423 node_index = mrMesh.rGetNodePermutation()[ rNodeIndices[requested_index] ];
00424 }
00425
00426
00427 std::vector<double> time_series = mpDataReader->GetVariableOverTime(variable_names[name_index], node_index);
00428 assert ( time_series.size() == mpDataReader->GetUnlimitedDimensionValues().size());
00429
00430
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
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
00452 if (PetscTools::AmMaster())
00453 {
00454
00455 p_file = output_file_handler.OpenOutputFile(rFileName);
00456 }
00457 else
00458 {
00459
00460 p_file = output_file_handler.OpenOutputFile(rFileName, std::ios::app);
00461 }
00462
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
00473 if (PetscTools::AmTopMost())
00474 {
00475 std::string comment = "# " + ChasteBuildInfo::GetProvenanceString();
00476 *p_file << comment;
00477 }
00478 p_file->close();
00479 }
00480
00481 PetscTools::EndRoundRobin();
00482 }
00483
00484
00486
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>;