Salome HOME
4a01e0238aebf22386b9030ab481800e968cd70d
[modules/paravis.git] / src / Plugins / MEDReader / plugin / MEDReaderIO / vtkMEDReader.cxx
1 // Copyright (C) 2010-2021  CEA/DEN, EDF R&D
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19 // Author : Anthony Geay
20
21 #include "vtkMEDReader.h"
22 #include "vtkGenerateVectors.h"
23 #include "MEDUtilities.hxx"
24
25 #include "vtkCellArray.h"
26 #include "vtkCellData.h"
27 #include "vtkCellType.h"
28 #include "vtkDataSetAttributes.h"
29 #include "vtkDataArraySelection.h"
30 #include "vtkDoubleArray.h"
31 #include "vtkExecutive.h"
32 #include "vtkInformation.h"
33 #include "vtkInformationDataObjectMetaDataKey.h"
34 #include "vtkInformationDoubleVectorKey.h"
35 #include "vtkInformationQuadratureSchemeDefinitionVectorKey.h"
36 #include "vtkInformationStringKey.h"
37 #include "vtkInformationVector.h"
38 #include "vtkMultiBlockDataSet.h"
39 #include "vtkMultiTimeStepAlgorithm.h"
40 #include "vtkMutableDirectedGraph.h"
41 #include "vtkObjectFactory.h"
42 #include "vtkPointData.h"
43 #include "vtkQuadratureSchemeDefinition.h"
44 #include "vtkSmartPointer.h"
45 #include "vtkStreamingDemandDrivenPipeline.h"
46 #include "vtkStringArray.h"
47 #include "vtkUnsignedCharArray.h"
48 #include "vtkUnstructuredGrid.h"
49 #include "vtkVariantArray.h"
50
51 #ifdef MEDREADER_USE_MPI
52 #include "vtkMultiProcessController.h"
53 #include "vtkPUnstructuredGridGhostCellsGenerator.h"
54 #endif
55
56 #include "MEDFileFieldRepresentationTree.hxx"
57
58 #include <map>
59 #include <string>
60 #include <vector>
61 #include <sstream>
62 #include <algorithm>
63
64 class vtkMEDReader::vtkMEDReaderInternal
65 {
66
67 public:
68   vtkMEDReaderInternal(vtkMEDReader *master):TK(0),IsMEDOrSauv(true),IsStdOrMode(false),GenerateVect(false),SIL(0),LastLev0(-1),GCGCP(true)
69   {
70   }
71
72   ~vtkMEDReaderInternal()
73   {
74     if(this->SIL)
75       this->SIL->Delete();
76   }
77 public:
78   MEDFileFieldRepresentationTree Tree;
79   vtkNew<vtkDataArraySelection> FieldSelection;
80   vtkNew<vtkDataArraySelection> TimeFlagSelection;
81
82   TimeKeeper TK;
83   std::string FileName;
84   //when true the file is MED file. when false it is a Sauv file
85   bool IsMEDOrSauv;
86   //when false -> std, true -> mode. By default std (false).
87   bool IsStdOrMode;
88   //when false -> do nothing. When true cut off or extend to nbOfCompo=3 vector arrays.
89   bool GenerateVect;
90   std::string DftMeshName;
91   // Store the vtkMutableDirectedGraph that represents links between family, groups and cell types
92   vtkMutableDirectedGraph* SIL;
93   // store the lev0 id in Tree corresponding to the TIME_STEPS in the pipeline.
94   int LastLev0;
95   bool GCGCP;
96 };
97
98 vtkStandardNewMacro(vtkMEDReader)
99
100 // vtkInformationKeyMacro(vtkMEDReader, META_DATA, DataObjectMetaData); // Here we need to customize vtkMEDReader::META_DATA method
101 // start of overload of vtkInformationKeyMacro
102 static vtkInformationDataObjectMetaDataKey *vtkMEDReader_META_DATA=new vtkInformationDataObjectMetaDataKey("META_DATA","vtkMEDReader");
103
104 vtkInformationDataObjectMetaDataKey *vtkMEDReader::META_DATA()  
105 {
106   static const char ZE_KEY[]="vtkMEDReader::META_DATA";
107   vtkInformationDataObjectMetaDataKey *ret(vtkMEDReader_META_DATA);
108   MEDCoupling::GlobalDict *gd(MEDCoupling::GlobalDict::GetInstance());
109   if(!gd->hasKey(ZE_KEY))
110     {// here META_DATA is put on global var to be exchanged with other filters without dependancy of MEDReader. Please do not change ZE_KEY !
111       std::ostringstream oss; oss << ret;
112       gd->setKeyValue(ZE_KEY,oss.str());
113     }
114   return ret;
115 }
116
117 static vtkInformationGaussDoubleVectorKey *vtkMEDReader_GAUSS_DATA=new vtkInformationGaussDoubleVectorKey("GAUSS_DATA","vtkMEDReader");
118
119 vtkInformationGaussDoubleVectorKey *vtkMEDReader::GAUSS_DATA()  
120 {
121   static const char ZE_KEY[]="vtkMEDReader::GAUSS_DATA";
122   vtkInformationGaussDoubleVectorKey *ret(vtkMEDReader_GAUSS_DATA);
123   MEDCoupling::GlobalDict *gd(MEDCoupling::GlobalDict::GetInstance());
124   if(!gd->hasKey(ZE_KEY))
125     {// here META_DATA is put on global var to be exchanged with other filters without dependancy of MEDReader. Please do not change ZE_KEY !
126       vtkInformationDoubleVectorKey *ret2(ret);
127       std::ostringstream oss; oss << ret2;
128       gd->setKeyValue(ZE_KEY,oss.str());
129     }
130   return ret;
131 }
132 // end of overload of vtkInformationKeyMacro
133
134 vtkMEDReader::vtkMEDReader():Internal(new vtkMEDReaderInternal(this))
135 {
136   this->SetNumberOfInputPorts(0);
137   this->SetNumberOfOutputPorts(1);
138 }
139
140 vtkMEDReader::~vtkMEDReader()
141 {
142   delete this->Internal;
143   this->Internal = 0;
144 }
145
146 void vtkMEDReader::Reload()
147 {
148   std::string fName((const char *)this->GetFileName());
149   delete this->Internal;
150   this->Internal=new vtkMEDReaderInternal(this);
151   this->SetFileName(fName.c_str());
152 }
153
154 void vtkMEDReader::GenerateVectors(int val)
155 {
156   if ( !this->Internal )
157     return;
158   
159   bool val2((bool)val);
160   if(val2!=this->Internal->GenerateVect)
161     {
162       this->Internal->GenerateVect=val2;
163       this->Modified();
164     }
165 }
166
167 void vtkMEDReader::ChangeMode(int newMode)
168 {
169   if ( !this->Internal )
170     return;
171   
172   this->Internal->IsStdOrMode=newMode!=0;
173   this->Modified();
174 }
175
176 void vtkMEDReader::GhostCellGeneratorCallForPara(int gcgcp)
177 {
178   if ( !this->Internal )
179     return;
180   
181   bool newVal(gcgcp!=0);
182   if(newVal!=this->Internal->GCGCP)
183     {
184       this->Internal->GCGCP=newVal;
185       this->Modified();
186     }
187 }
188
189 const char *vtkMEDReader::GetSeparator()
190 {
191   return MEDFileFieldRepresentationLeavesArrays::ZE_SEP;
192 }
193
194 void vtkMEDReader::SetFileName(const char *fname)
195 {
196   if(!this->Internal)
197     return;
198   try
199     {
200       this->Internal->FileName=fname;
201       this->Modified();
202     }
203   catch(INTERP_KERNEL::Exception& e)
204     {
205       delete this->Internal;
206       this->Internal=0;
207       std::ostringstream oss;
208       oss << "Exception has been thrown in vtkMEDReader::SetFileName : " << e.what() << std::endl;
209       if(this->HasObserver("ErrorEvent") )
210         this->InvokeEvent("ErrorEvent",const_cast<char *>(oss.str().c_str()));
211       else
212         vtkOutputWindowDisplayErrorText(const_cast<char *>(oss.str().c_str()));
213       vtkObject::BreakOnError();
214     }
215 }
216
217 char *vtkMEDReader::GetFileName()
218 {
219   if (!this->Internal)
220     return 0;
221   return const_cast<char *>(this->Internal->FileName.c_str());
222 }
223
224 int vtkMEDReader::RequestInformation(vtkInformation *request, vtkInformationVector ** /*inputVector*/, vtkInformationVector *outputVector)
225 {
226 //  std::cout << "########################################## vtkMEDReader::RequestInformation ##########################################" << std::endl;
227   if(!this->Internal)
228     return 0;
229   try
230     {
231       // Process file meta data
232       std::size_t pos(this->Internal->FileName.find_last_of('.'));
233       if(pos!=std::string::npos)
234         {
235           std::string ext(this->Internal->FileName.substr(pos));
236           if(ext.find("sauv")!=std::string::npos)
237             this->Internal->IsMEDOrSauv=false;
238         }
239       if(this->Internal->Tree.getNumberOfLeavesArrays()==0)
240         {
241           int iPart(-1),nbOfParts(-1);
242 #ifdef MEDREADER_USE_MPI
243           vtkMultiProcessController *vmpc(vtkMultiProcessController::GetGlobalController());
244           if(vmpc)
245             {
246               iPart=vmpc->GetLocalProcessId();
247               nbOfParts=vmpc->GetNumberOfProcesses();
248             }
249 #endif
250           this->Internal->Tree.loadMainStructureOfFile(this->Internal->FileName.c_str(),this->Internal->IsMEDOrSauv,iPart,nbOfParts);
251           
252           // Leaves
253           this->Internal->Tree.activateTheFirst();//This line manually initialize the status of server (this) with the remote client.
254           for (int idLeaveArray = 0; idLeaveArray < this->Internal->Tree.getNumberOfLeavesArrays(); idLeaveArray++)
255           {
256             std::string name = this->Internal->Tree.getNameOf(idLeaveArray);
257             bool status = this->Internal->Tree.getStatusOf(idLeaveArray);
258             this->Internal->FieldSelection->AddArray(name.c_str(), status);
259           }
260         }
261
262       // Time flags
263       this->Internal->TK.setMaxNumberOfTimeSteps(this->Internal->Tree.getMaxNumberOfTimeSteps());
264       auto timeFlagsArray = this->Internal->TK.getTimesFlagArray();
265       for (int idTimeFlag = 0; idTimeFlag < timeFlagsArray.size() ; idTimeFlag++)
266       {
267         std::string name = timeFlagsArray[idTimeFlag].second;
268         bool status = timeFlagsArray[idTimeFlag].first;
269         this->Internal->TimeFlagSelection->AddArray(name.c_str(), status);
270       }
271
272       // Make sure internal model are synchronized
273       /// So the SIL is up to date
274       int nArrays = this->Internal->FieldSelection->GetNumberOfArrays();
275       for(int i = nArrays - 1; i >= 0; i--)
276       {
277         try
278         {
279         this->Internal->Tree.changeStatusOfAndUpdateToHaveCoherentVTKDataSet(
280           this->Internal->Tree.getIdHavingZeName(this->Internal->FieldSelection->GetArrayName(i)),
281           this->Internal->FieldSelection->GetArraySetting(i));
282         }
283         catch(INTERP_KERNEL::Exception& e)
284         {
285           // Remove the incorrect array
286           this->Internal->FieldSelection->RemoveArrayByIndex(i);
287         }
288       }
289
290 //      request->Print(cout);
291       vtkInformation *outInfo(outputVector->GetInformationObject(0));
292       outInfo->Set(vtkDataObject::DATA_TYPE_NAME(),"vtkMultiBlockDataSet");
293       this->UpdateSIL(request, outInfo);
294
295       // Set the meta data graph as a meta data key in the information
296       // That's all that is needed to transfer it along the pipeline
297       outInfo->Set(vtkMEDReader::META_DATA(),this->Internal->SIL);
298
299       bool dummy(false);
300       this->PublishTimeStepsIfNeeded(outInfo,dummy);
301     }
302   catch(INTERP_KERNEL::Exception& e)
303     {
304       std::ostringstream oss;
305       oss << "Exception has been thrown in vtkMEDReader::RequestInformation : " << e.what() << std::endl;
306       if(this->HasObserver("ErrorEvent") )
307         this->InvokeEvent("ErrorEvent",const_cast<char *>(oss.str().c_str()));
308       else
309         vtkOutputWindowDisplayErrorText(const_cast<char *>(oss.str().c_str()));
310       vtkObject::BreakOnError();
311       return 0;
312     }
313   return 1;
314 }
315
316 int vtkMEDReader::RequestData(vtkInformation *request, vtkInformationVector ** /*inputVector*/, vtkInformationVector *outputVector)
317 {
318 //  std::cout << "########################################## vtkMEDReader::RequestData ##########################################" << std::endl;
319   if(!this->Internal)
320     return 0;
321   try
322   {
323       for(int i = 0; i < this->Internal->FieldSelection->GetNumberOfArrays(); i++)
324       {
325         this->Internal->Tree.changeStatusOfAndUpdateToHaveCoherentVTKDataSet(
326           this->Internal->Tree.getIdHavingZeName(this->Internal->FieldSelection->GetArrayName(i)), 
327           this->Internal->FieldSelection->GetArraySetting(i));
328       }
329           
330       auto& timeFlagsArray = this->Internal->TK.getTimesFlagArray();
331       if (timeFlagsArray.size() != this->Internal->TimeFlagSelection->GetNumberOfArrays())
332       {
333         throw INTERP_KERNEL::Exception("Unexpected size of TimeFlagSelection");
334       }
335       for(int i = 0; i < this->Internal->TimeFlagSelection->GetNumberOfArrays(); i++)
336       {
337         timeFlagsArray[i] = std::make_pair(this->Internal->TimeFlagSelection->GetArraySetting(i), 
338           this->Internal->TimeFlagSelection->GetArrayName(i));
339       }
340
341 //      request->Print(cout);
342       vtkInformation *outInfo(outputVector->GetInformationObject(0));
343       vtkMultiBlockDataSet *output(vtkMultiBlockDataSet::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT())));
344       //bool isUpdated(false); // todo: unused
345       double reqTS(0.);
346       if(outInfo->Has(vtkStreamingDemandDrivenPipeline::UPDATE_TIME_STEP()))
347         reqTS=outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_TIME_STEP());
348       ExportedTinyInfo ti;
349 #ifndef MEDREADER_USE_MPI
350       this->FillMultiBlockDataSetInstance(output,reqTS,&ti);
351 #else
352       if(this->Internal->GCGCP)
353         {
354           vtkSmartPointer<vtkPUnstructuredGridGhostCellsGenerator> gcg(vtkSmartPointer<vtkPUnstructuredGridGhostCellsGenerator>::New());
355           {
356             vtkDataSet *ret(RetrieveDataSetAtTime(reqTS,&ti));
357             gcg->SetInputData(ret);
358             ret->Delete();
359           }
360           gcg->SetUseGlobalPointIds(true);
361           gcg->SetBuildIfRequired(false);
362           gcg->Update();
363           output->SetBlock(0,gcg->GetOutput());
364         }
365       else
366         this->FillMultiBlockDataSetInstance(output,reqTS,&ti);
367 #endif
368       if(!ti.empty())
369         {
370           const std::vector<double>& data(ti.getData());
371           outInfo->Set(vtkMEDReader::GAUSS_DATA(),&data[0],(int)data.size());
372           request->Append(vtkExecutive::KEYS_TO_COPY(),vtkMEDReader::GAUSS_DATA());// Thank you to SciberQuest and DIPOLE_CENTER ! Don't understand why ! In RequestInformation it does not work !
373         }
374       output->GetInformation()->Set(vtkDataObject::DATA_TIME_STEP(),reqTS);
375       // Is it really needed ? TODO
376       this->UpdateSIL(request, outInfo);
377     }
378   catch(INTERP_KERNEL::Exception& e)
379     {
380       std::cerr << "Exception has been thrown in vtkMEDReader::RequestData : " << e.what() << std::endl;
381       return 0;
382     }
383   return 1;
384 }
385
386 //------------------------------------------------------------------------------
387 int vtkMEDReader::GetNumberOfFieldsTreeArrays()
388 {
389   return this->Internal->FieldSelection->GetNumberOfArrays();
390 }
391
392 //------------------------------------------------------------------------------
393 const char* vtkMEDReader::GetFieldsTreeArrayName(int index)
394 {
395   return this->Internal->FieldSelection->GetArrayName(index);
396 }
397
398 //------------------------------------------------------------------------------
399 int vtkMEDReader::GetFieldsTreeArrayStatus(const char* name)
400 {
401   return this->Internal->FieldSelection->ArrayIsEnabled(name);
402 }
403
404 //------------------------------------------------------------------------------
405 void vtkMEDReader::SetFieldsStatus(const char* name, int status)
406 {
407   if (this->GetFieldsTreeArrayStatus(name) != status)
408   {
409     if (status)
410     {
411       this->Internal->FieldSelection->EnableArray(name);
412     }
413     else
414     {
415       this->Internal->FieldSelection->DisableArray(name);
416     }
417     this->Modified();
418   }
419 }
420
421 //------------------------------------------------------------------------------
422 int vtkMEDReader::GetNumberOfTimesFlagsArrays()
423 {
424   return this->Internal->TimeFlagSelection->GetNumberOfArrays();
425 }
426
427 //------------------------------------------------------------------------------
428 const char* vtkMEDReader::GetTimesFlagsArrayName(int index)
429 {
430   return this->Internal->TimeFlagSelection->GetArrayName(index);
431 }
432
433 //------------------------------------------------------------------------------
434 int vtkMEDReader::GetTimesFlagsArrayStatus(const char* name)
435 {
436   return this->Internal->TimeFlagSelection->ArrayIsEnabled(name);
437 }
438
439 //------------------------------------------------------------------------------
440 void vtkMEDReader::SetTimesFlagsStatus(const char* name, int status)
441 {
442   if (this->GetTimesFlagsArrayStatus(name) != status)
443   {
444     if (status)
445     {
446       this->Internal->TimeFlagSelection->EnableArray(name);
447     }
448     else
449     {
450       this->Internal->TimeFlagSelection->DisableArray(name);
451     }
452     this->Modified();
453   }
454 }
455
456 void vtkMEDReader::UpdateSIL(vtkInformation* /*request*/, vtkInformation * /*info*/)
457 {
458   if(!this->Internal)
459       return;
460   std::string meshName(this->Internal->Tree.getActiveMeshName());
461   if(!this->Internal->SIL || meshName!=this->Internal->DftMeshName)
462     {
463       vtkMutableDirectedGraph *sil(vtkMutableDirectedGraph::New());
464       this->BuildSIL(sil);
465       if(this->Internal->SIL)
466         this->Internal->SIL->Delete();
467       this->Internal->SIL=sil;
468       this->Internal->DftMeshName=meshName;
469     }
470 }
471
472 /*!
473  * The returned string is the name of the mesh activated which groups and families are in \a sil.
474  */
475 std::string vtkMEDReader::BuildSIL(vtkMutableDirectedGraph* sil)
476 {
477   if (!this->Internal)
478     return std::string();
479   sil->Initialize();
480   vtkSmartPointer<vtkVariantArray> childEdge(vtkSmartPointer<vtkVariantArray>::New());
481   childEdge->InsertNextValue(0);
482   vtkSmartPointer<vtkVariantArray> crossEdge(vtkSmartPointer<vtkVariantArray>::New());
483   crossEdge->InsertNextValue(1);
484   // CrossEdge is an edge linking hierarchies.
485   vtkUnsignedCharArray* crossEdgesArray=vtkUnsignedCharArray::New();
486   crossEdgesArray->SetName("CrossEdges");
487   sil->GetEdgeData()->AddArray(crossEdgesArray);
488   crossEdgesArray->Delete();
489   std::vector<std::string> names;
490   // Now build the hierarchy.
491   vtkIdType rootId=sil->AddVertex();
492   names.push_back("SIL");
493   // Add global fields root
494   vtkIdType fieldsRoot(sil->AddChild(rootId,childEdge));
495   names.push_back("FieldsStatusTree");
496   this->Internal->Tree.feedSIL(sil,fieldsRoot,childEdge,names);
497   vtkIdType meshesFamsGrpsRoot(sil->AddChild(rootId,childEdge));
498   names.push_back("MeshesFamsGrps");
499   std::string dftMeshName(this->Internal->Tree.feedSILForFamsAndGrps(sil,meshesFamsGrpsRoot,childEdge,names));
500   // This array is used to assign names to nodes.
501   vtkStringArray *namesArray(vtkStringArray::New());
502   namesArray->SetName("Names");
503   namesArray->SetNumberOfTuples(sil->GetNumberOfVertices());
504   sil->GetVertexData()->AddArray(namesArray);
505   namesArray->Delete();
506   std::vector<std::string>::const_iterator iter;
507   vtkIdType cc;
508   for(cc=0, iter=names.begin(); iter!=names.end(); ++iter, ++cc)
509     namesArray->SetValue(cc,(*iter).c_str());
510   return dftMeshName;
511 }
512
513 double vtkMEDReader::PublishTimeStepsIfNeeded(vtkInformation *outInfo, bool& isUpdated)
514 {
515   if(!this->Internal)
516     return 0.0;
517
518   int lev0(-1);
519   std::vector<double> tsteps;
520   if(!this->Internal->IsStdOrMode)
521     tsteps=this->Internal->Tree.getTimeSteps(lev0,this->Internal->TK);
522   else
523     { tsteps.resize(1); tsteps[0]=0.; }
524   isUpdated=false;
525   if(lev0!=this->Internal->LastLev0)
526     {
527       isUpdated=true;
528       double timeRange[2];
529       timeRange[0]=tsteps.front();
530       timeRange[1]=tsteps.back();
531       outInfo->Set(vtkStreamingDemandDrivenPipeline::TIME_STEPS(),&tsteps[0],(int)tsteps.size());
532       outInfo->Set(vtkStreamingDemandDrivenPipeline::TIME_RANGE(),timeRange,2);
533       this->Internal->LastLev0=lev0;
534     }
535   return tsteps.front();
536 }
537
538 void vtkMEDReader::FillMultiBlockDataSetInstance(vtkMultiBlockDataSet *output, double reqTS, ExportedTinyInfo *internalInfo)
539 {
540   if( !this->Internal )
541     return;
542   vtkDataSet *ret(RetrieveDataSetAtTime(reqTS,internalInfo));
543   output->SetBlock(0,ret);
544   ret->Delete();
545 }
546
547 vtkDataSet *vtkMEDReader::RetrieveDataSetAtTime(double reqTS, ExportedTinyInfo *internalInfo)
548 {
549   if( !this->Internal )
550     return 0;
551   std::string meshName;
552   vtkDataSet *ret(this->Internal->Tree.buildVTKInstance(this->Internal->IsStdOrMode,reqTS,meshName,this->Internal->TK,internalInfo));
553   if(this->Internal->GenerateVect)
554     {
555       vtkGenerateVectors::Operate(ret->GetPointData());
556       vtkGenerateVectors::Operate(ret->GetCellData());
557       vtkGenerateVectors::Operate(ret->GetFieldData());
558       // The operations above have potentially created new arrays -> This breaks the optimization of StaticMesh that expects the same field arrays over time.
559       // To enforce the cache recomputation declare modification of mesh.
560       //vtkGenerateVectors::ChangeMeshTimeToUpdateCache(ret);
561     }
562   return ret;
563 }
564
565 void vtkMEDReader::PrintSelf(ostream& os, vtkIndent indent)
566 {
567   this->Superclass::PrintSelf(os, indent);
568 }