Chaste Release::3.1
|
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>;