CEPS  24.01
Cardiac ElectroPhysiology Simulator
MeditGeometryWriter.cpp
Go to the documentation of this file.
1 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2  This file is part of CEPS.
3 
4  CEPS is free software: you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation, either version 3 of the License, or
7  (at your option) any later version.
8 
9  CEPS is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  GNU General Public License for more details.
13 
14  You should have received a copy of the GNU General Public License
15  along with CEPS (see file LICENSE at root of project).
16  If not, see <https://www.gnu.org/licenses/>.
17 
18 
19  Copyright 2019-2024 Inria, Universite de Bordeaux
20 
21  Authors, in alphabetical order:
22 
23  Pierre-Elliott BECUE, Florian CARO, Yves COUDIERE(*), Andjela DAVIDOVIC,
24  Charlie DOUANLA-LONTSI, Marc FUENTES, Mehdi JUHOOR, Michael LEGUEBE(*),
25  Pauline MIGERDITICHAN, Valentin PANNETIER(*), Nejib ZEMZEMI.
26  * : currently active authors
27 
28 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
31 #include "MeditGeometryWriter.hpp"
32 
34 MeditGeometryWriter (const CepsString &fileName, Geometry *geom) :
35  FileWriter (fileName),
36  m_showGeometryPartitioning (false)
37 {
38  m_geom = geom;
40  "Geometry passed to MeditGeometryWriter is nullptr"
41  );
42 }
43 
46 {
47 }
48 
51 {
53 }
54 
56 write()
57 {
58  if (ceps::isMaster ())
59  {
60  open();
61  writeHeader();
63  CEPS_SAYS("Writing mesh in " << m_fileName);
64  }
65  writeNodes();
66  writeCells();
67  if (ceps::isMaster())
68  close();
69 }
70 
72 writeHeader ()
73 {
74  m_file << "MeshVersionFormatted 2" << std::endl;
75  m_file << "Dimension" << std::endl << "3" << std::endl;
76 }
77 
79 writeNodes()
80 {
81  CepsUInt nbNodes = m_geom->getNbNodes();
82  MPI_Comm comm = ceps::getCommunicator();
83 
84  CepsVector<CepsReal> coords(nbNodes*3,0.),myCoords(nbNodes*3,0.);
85  CepsVector<CepsInt > attrs (nbNodes ,0 ),myAttrs (nbNodes ,0 );
86 
87  // Gather nodes data
88  // FIXPERF
89  // As the partitioning may not place the CPUs in order, it would be really cumbersome
90  // to do an all gather. So for now, we can do ann AllReduce
91  for (CepsUInt dim=1;dim<=3;dim++)
92  if (m_geom->hasMeshOfDim(dim))
93  {
95  for (GeomNode* n: nodes)
96  {
97  CepsNodeGlobalIndex nID = n->getGlobalIndex();
98  myCoords[3*nID ] = n->x();
99  myCoords[3*nID+1] = n->y();
100  myCoords[3*nID+2] = n->z();
102  myAttrs[nID] = ceps::getRank();
103  else
104  // Medit format has only one attribute, so we use the first one only
105  myAttrs[nID] = *(n->getAttributes().begin());
106  }
107  }
108 
109 
110  MPI_Allreduce(myCoords.data(),coords.data(),nbNodes*3,CEPS_MPI_REAL,MPI_SUM,comm);
111  MPI_Allreduce(myAttrs .data(),attrs .data(),nbNodes ,CEPS_MPI_INT ,MPI_SUM,comm);
112 
113  // Now master writes the nodes
114  // We do it with a buffer
115  if (ceps::isMaster ())
116  {
117  m_file << std::endl << "Vertices" << std::endl << nbNodes << std::endl;
118  std::stringstream toWrite (std::stringstream::out);
119  toWrite.unsetf (std::ios::floatfield);
120  toWrite.precision (15);
121  CepsInt counter = 0;
122  for (CepsUInt i = 0; i < nbNodes; i++, counter++)
123  {
124  toWrite << coords[i*3 ] << " "
125  << coords[i*3+1] << " "
126  << coords[i*3+2] << " "
127  << attrs [i] << std::endl;
128 
129  if (counter > 4096)
130  {
131  m_file.write(toWrite.str().c_str(),toWrite.str().length());
132  toWrite.str("");
133  counter = 0;
134  }
135  }
136  m_file.write(toWrite.str().c_str(),toWrite.str().length());
137  }
138 }
139 
141 writeCells ()
142 {
143  CepsBool surfacicCellsWritten = false;
144  CepsBool cableCellsWritten = false;
145  CepsUInt nCells;
146  Mesh* mesh;
147 
148  // 3D cells
149  if (m_geom->has3dMesh())
150  {
151  mesh = m_geom->get3dMesh();
152  nCells = mesh->getNbCells();
153  if (ceps::isMaster())
154  m_file << std::endl << "Tetrahedra" << std::endl << nCells << std::endl;
155 
156  this->writeCellsOf(mesh);
157 
158  // 2D boundary cells of 3d mesh
159  nCells = mesh->getNbBoundaryCells();
160  if (nCells)
161  {
162  // Maybe there is a 2d mesh, so get it's number of cells
163  if (m_geom->has2dMesh())
164  nCells += (m_geom->get2dMesh())->getNbCells();
165 
166  m_file << std::endl;
167  m_file << "Triangles" << std::endl << nCells << std::endl;
168  surfacicCellsWritten = true;
169  this->writeCellsOf (mesh,true);
170  }
171  }
172 
173  // 2D cells of 2d mesh
174  if (m_geom->has2dMesh ())
175  {
176  mesh = m_geom->get2dMesh();
177  nCells = mesh->getNbCells();
178  if (!surfacicCellsWritten && ceps::isMaster())
179  m_file << std::endl << "Triangles" << std::endl << nCells << std::endl;
180 
181  this->writeCellsOf(mesh);
182 
183  // 1D boundary cells of 2D mesh
184  nCells = mesh->getNbBoundaryCells();
185  if (nCells)
186  {
187  if (m_geom->has1dMesh())
188  nCells += (m_geom->get1dMesh())->getNbCells();
189 
190  m_file << std::endl;
191  m_file << "Edges" << std::endl << nCells << std::endl;
192  cableCellsWritten = true;
193  this->writeCellsOf(mesh,true);
194  }
195  }
196 
197  // 1D cells of 1d mesh
198  if (m_geom->has1dMesh ())
199  {
200  mesh = m_geom->get1dMesh();
201  nCells = mesh->getNbCells();
202  if (! cableCellsWritten && ceps::isMaster())
203  m_file << std::endl << "Edges" << std::endl << nCells << std::endl;
204 
205  this->writeCellsOf(mesh);
206  }
207 }
208 
210 writeCellsOf(Mesh *mesh, CepsBool boundary)
211 {
212  CepsUInt nbLocalCells = boundary ? mesh->getLocalNbBoundaryCells()
213  : mesh->getLocalNbCells();
214  auto cells = boundary ? mesh->getBoundaryCells()
215  : mesh->getCells();
216  CepsUInt gridSize = ceps::getGridSize();
218  CepsUInt rank = ceps::getRank();
219  MPI_Comm comm = ceps::getCommunicator();
220 
221  CepsVector<CepsNodeGlobalIndex> cellNodeIndicesToSend;
222  CepsVector<CepsInt> cellAttrToSend;
223  CepsVector<CepsCellGlobalIndex> cellIndicesToSend;
224 
225  // the array that will hold the number of local cells per process
226  CepsUInt *nbCellsArray = new CepsUInt[gridSize];
227  nbCellsArray[rank] = nbLocalCells;
228  // each process sends 1 CepsUInt to the root
229  MPI_Gather(&nbLocalCells,1,CEPS_MPI_UINT,nbCellsArray,1,CEPS_MPI_UINT,0,comm);
230 
231  // Try to reserve some memory for better performance.
232  // sizeHint is the number of nodes of the first cell
233  CepsUInt sizeHint = 0;
234  if (nbLocalCells)
235  sizeHint = cells[0]->getNumberOfNodes();
236 
237  // If the meshes are very small (nb cells < nb process) then it is
238  // possible that the root process does not own any cells. In that
239  // case, sizeHint_t is zero, and we will have a problem when trying
240  // to write the cells. To make sure that root process is aware of
241  // the number of nodes per cell, exchange this information across
242  // the computing grid.
243  CepsUInt *sizeHintArray = new CepsUInt[gridSize];
244  sizeHintArray[rank] = sizeHint;
245  MPI_Gather(&sizeHint,1,CEPS_MPI_UINT,sizeHintArray,1,CEPS_MPI_UINT,0,comm);
246  if (isMaster)
247  {
248  CepsUInt max = sizeHintArray[0];
249  for (CepsUInt i = 1U; i < gridSize; i++)
250  if (sizeHintArray[i] > max)
251  max = sizeHintArray[i];
252  sizeHint = max;
253  }
254 
255  cellNodeIndicesToSend.reserve (nbLocalCells*sizeHint);
256  cellAttrToSend .reserve (nbLocalCells);
257  cellIndicesToSend .reserve (nbLocalCells);
258 
259  CepsUInt size = cells.size ();
260  GeomCell *cell;
262  for (CepsUInt i = 0U; i < size; i++)
263  {
264  nodeIndices.clear ();
265  cell = cells[i];
266  nodeIndices = cell->getNodeIndices();
267  // insert the node indices
268  cellNodeIndicesToSend.insert(cellNodeIndicesToSend.end(),
269  nodeIndices.begin(),nodeIndices.end());
270  // push_back this cell's attribute. We can only take the first.
271  cellAttrToSend.push_back(*(cell->getAttributes().begin()));
272  // push_back this cell's globalIndex
273  cellIndicesToSend.push_back((CepsCellGlobalIndex)cell->getGlobalIndex());
274  }
275 
276  // Communicate the sizes of the vectors
277 
278  // Cell indices first
279  int *cellIndicesToSendSizes = new int[gridSize];
280  int localIndicesSize = (int) cellIndicesToSend.size ();
281  cellIndicesToSendSizes[rank] = localIndicesSize;
282  MPI_Gather(&localIndicesSize,1,MPI_INT,cellIndicesToSendSizes,1,MPI_INT,0,comm);
283 
284  // Cell node indices
285  int *cellNodeIndicesToSendSizes = new int[gridSize];
286  int localNodeIndicesSize = (int) cellNodeIndicesToSend.size ();
287  cellNodeIndicesToSendSizes[rank] = localNodeIndicesSize;
288  MPI_Gather(&localNodeIndicesSize,1,MPI_INT,cellNodeIndicesToSendSizes,1,MPI_INT,0,comm);
289 
290  // Allocate the space needed on master process
291  // These variable will be relevant on root process only
292  CepsUInt *cellIndices = nullptr;
293  CepsUInt *cellNodeIndices = nullptr;
294  CepsInt *cellAttr = nullptr;
295  CepsUInt cellIndicesTotalSize = 0, cellNodeIndicesTotalSize = 0;
296  if (isMaster)
297  {
298  for (CepsUInt i = 0; i < gridSize; i++)
299  {
300  cellIndicesTotalSize += cellIndicesToSendSizes[i];
301  cellNodeIndicesTotalSize += cellNodeIndicesToSendSizes[i];
302  }
303  cellIndices = new CepsUInt[cellIndicesTotalSize];
304  cellNodeIndices = new CepsUInt[cellNodeIndicesTotalSize];
305  cellAttr = new CepsInt[cellIndicesTotalSize];
306  }
307 
308  // Now send the 'real' data: cell indices and cell nodes indices
309 
310  // Start with cell indices.
311  // Create the displacement array
312  int* displ = new int[gridSize];
313  displ[0] = 0;
314  if (isMaster)
315  for (CepsUInt i = 1; i < gridSize; i++)
316  displ[i] = displ[i-1] + cellIndicesToSendSizes[i-1];
317 
318  MPI_Gatherv(cellIndicesToSend.data(),cellIndicesToSendSizes[rank],CEPS_MPI_UINT,
319  cellIndices,cellIndicesToSendSizes,displ,CEPS_MPI_UINT,0,comm);
320 
321  // similar for attributes
322  MPI_Gatherv(cellAttrToSend.data(),cellIndicesToSendSizes[rank],CEPS_MPI_INT,
323  cellAttr,cellIndicesToSendSizes,displ,CEPS_MPI_INT,0,comm);
324 
325  // Now, send the node indices.
326  // Update displacement array
327  if (isMaster)
328  for (CepsUInt i = 1; i < gridSize; i++)
329  displ[i] = displ[i-1] + cellNodeIndicesToSendSizes[i-1];
330 
331  MPI_Gatherv(cellNodeIndicesToSend.data(),cellNodeIndicesToSendSizes[rank],CEPS_MPI_UINT,
332  cellNodeIndices,cellNodeIndicesToSendSizes,displ,CEPS_MPI_UINT,0,comm);
333 
334  if (isMaster)
335  {
336  // build the array of cell offsets
337  CepsUInt *cellOffset = new CepsUInt[gridSize+1];
338  cellOffset[0] = 0;
339  for (CepsUInt i = 1; i <= gridSize; i++)
340  {
341  cellOffset[i] = cellOffset[i-1] + cellIndicesToSendSizes[i-1];
342  }
343 
344  std::stringstream toWrite(std::stringstream::out);
345  std::set<CepsUInt> duplicates;
346  CepsUInt index, counter=0u;
347  // iterate through our array, by parts
348  for (CepsUInt j = 0; j < cellIndicesTotalSize; j++)
349  {
350  index = cellIndices[j];
351  if (duplicates.find (index) == duplicates.end ())
352  {
353  // write it in new order
354  for (CepsUInt k=0;k<sizeHint;k++)
355  {
356  CepsNodeGlobalIndex nID = cellNodeIndices[(j*sizeHint)+k];
357  toWrite << nID+1 << " ";
358  }
359  // write a node attribute, either the owning proc index
360  // or the true attribute
362  toWrite << this->determineCellOwner(cellOffset,j) << std::endl;
363  else
364  toWrite << cellAttr[j] << std::endl;
365 
366  duplicates.insert(index);
367  }
368  counter++;
369 
370  // Buffered writing: flush
371  if (counter == 4096)
372  {
373  m_file.write(toWrite.str().c_str(),toWrite.str().length());
374  toWrite.str ("");
375  counter = 0;
376  }
377  }
378  m_file.write(toWrite.str().c_str(),toWrite.str().length());
379  ceps::destroyTabular(cellOffset);
380  ceps::destroyTabular(displ);
381  }
382  // free memory
383  ceps::destroyTabular(cellNodeIndicesToSendSizes);
384  ceps::destroyTabular(cellIndicesToSendSizes);
385 
386  if (ceps::isMaster())
387  {
388  ceps::destroyTabular(cellIndices);
389  ceps::destroyTabular(cellNodeIndices);
390  ceps::destroyTabular(cellAttr);
391  }
392 }
393 
395 determineCellOwner(CepsUInt *cellOffset, CepsUInt index)
396 {
397  CepsUInt gridSize = ceps::getGridSize ();
398  CepsUInt proc = 0;
399  while(proc<gridSize and index>= cellOffset[proc+1])
400  proc++;
401  return proc;
402 
403  //for (CepsUInt proc = 0; proc < gridSize; proc++)
404  // if (index < cellOffset[proc+1])
405  // // proc was the owner of the cell
406  // return proc;
407 
408  // NEVER REACHED
409  //return 0;
410 }
#define CEPS_SAYS(message)
Writes a message in the debug log and in the terminal (stdio).
#define CEPS_ABORT_IF(condition, message)
Stops the execution with a message if condition is true. If testing is enabled, only throws a runtime...
#define CEPS_SEPARATOR
Writes a separator in the debug log and in the terminal.
std::basic_string< CepsChar > CepsString
C++ format string.
Definition: CepsTypes.hpp:128
CepsGlobalIndex CepsCellGlobalIndex
Indices of cells.
Definition: CepsTypes.hpp:222
#define CEPS_MPI_INT
Definition: CepsTypes.hpp:320
std::vector< _Type, _Alloc > CepsVector
C++ vector.
Definition: CepsTypes.hpp:155
bool CepsBool
Booleans.
Definition: CepsTypes.hpp:124
#define CEPS_MPI_REAL
Definition: CepsTypes.hpp:315
std::make_unsigned_t< CepsInt > CepsUInt
Unsigned version on CepsInt.
Definition: CepsTypes.hpp:109
CepsGlobalIndex CepsNodeGlobalIndex
Indices of nodes.
Definition: CepsTypes.hpp:224
int32_t CepsInt
Need 32 bit integer.
Definition: CepsTypes.hpp:106
#define CEPS_MPI_UINT
Definition: CepsTypes.hpp:325
Enables the writing of files.
Definition: FileWriter.hpp:40
void open()
Creates file or cleans previous content.
Definition: FileWriter.cpp:59
CepsString m_fileName
file to write
Definition: FileWriter.hpp:98
std::ofstream m_file
corresponding stream
Definition: FileWriter.hpp:99
void close()
Close current file.
Definition: FileWriter.cpp:77
Abstract class for geometrical cell. On top of index and attributes managament, the cell has informat...
Definition: GeomCell.hpp:48
Base class for nodes used in meshes.
Definition: GeomNode.hpp:53
Encapsulates all the geometrical data.
Definition: Geometry.hpp:50
CepsBool has1dMesh() const
true if geometry has 1d data
Definition: Geometry.cpp:188
Mesh * get3dMesh() const
Pointer on the 3D mesh.
Definition: Geometry.cpp:149
Mesh * get2dMesh() const
Pointer on the 2D mesh.
Definition: Geometry.cpp:155
CepsBool has2dMesh() const
true if geometry has 2d data
Definition: Geometry.cpp:182
CepsBool has3dMesh() const
true if geometry has 3d data
Definition: Geometry.cpp:176
Mesh * get1dMesh() const
Pointer on the 1D mesh.
Definition: Geometry.cpp:161
Mesh * getMeshOfDim(CepsUInt dim) const
Return the mesh of requested dimension.
Definition: Geometry.cpp:139
CepsUInt getNbNodes() const
Number of nodes of the geometry (global)
Definition: Geometry.cpp:383
CepsBool hasMeshOfDim(CepsUInt dim) const
true if geometry has data of requested dimension
Definition: Geometry.cpp:167
CepsBool m_showGeometryPartitioning
Replaces attributes with owners rank.
void writeHeader()
Medit headers are written using this method.
void showGeometryPartitioning(CepsBool flag)
If true, cell attribute is replaced by owning process rank. (default is false)
void write()
Writes mesh.
CepsUInt determineCellOwner(CepsUInt *cellOffset, CepsUInt index)
Get index of owner of cell (owner of first node)
void writeCells()
Writes cells and boundary cells with attributes.
void writeCellsOf(Mesh *mesh, CepsBool boundary=false)
Gather and write the cells.
MeditGeometryWriter(const CepsString &fileName, Geometry *geom)
Constructor with ouput file name and linked geometry.
~MeditGeometryWriter()
Destructor.
void writeNodes()
Writes node coordinates and attribute.
Geometry * m_geom
Geometry to write.
Geometrical information of 1,2 or 3D distributed cells.
Definition: Mesh.hpp:57
const CepsVector< GeomNode * > & getOwnedNodes()
CepsVector of local nodes stored on this process.
Definition: Mesh.cpp:216
CepsUInt getNbBoundaryCells() const
Total number of boundary cells of the mesh.
Definition: Mesh.cpp:234
CepsUInt getLocalNbCells() const
Number of non-boundary cells stored on this process.
Definition: Mesh.cpp:246
const CepsVector< GeomCell * > & getCells()
CepsVector of cells stored on this process.
Definition: Mesh.cpp:204
CepsUInt getNbCells() const
Total number of non-boundary cells of the mesh.
Definition: Mesh.cpp:228
CepsUInt getLocalNbBoundaryCells() const
Number of boundary cells stored on this process.
Definition: Mesh.cpp:252
const CepsVector< GeomCell * > & getBoundaryCells()
CepsVector of boundary cells stored on this process.
Definition: Mesh.cpp:210
CepsSet< CepsAttribute > & getAttributes()
Returns the attributes of the entity.
const CepsGlobalIndex & getGlobalIndex() const
Get the index
CepsVector< CepsGlobalIndex > getNodeIndices() const
Get indices of all nodes.
CepsUInt getRank()
Returns current processor rank.
void destroyTabular(_Type &)
Destroy[delete] any type.
Definition: CepsMemory.hpp:149
MPI_Comm getCommunicator()
Get the communicator.
const _Type & max(const CepsVector< _Type, _Alloc > &vec)
Returns the maximum of the vector, const version.
Definition: CepsVector.hpp:464
CepsUInt getGridSize()
Returns the number of process on the computing grid.
CepsBool isNullPtr(_Type *ptr)
Tells if passed pointer is null.
Definition: CepsMemory.hpp:45
CepsBool isMaster()
Is calling process the master ?