1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2020 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3 
4 // This library is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU Lesser General Public
6 // License as published by the Free Software Foundation; either
7 // version 2.1 of the License, or (at your option) any later version.
8 
9 // This library 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 GNU
12 // Lesser General Public License for more details.
13 
14 // You should have received a copy of the GNU Lesser General Public
15 // License along with this library; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17 
18 
19 
20 // libMesh includes
21 #include "libmesh/dof_map.h"
22 #include "libmesh/ensight_io.h"
23 #include "libmesh/equation_systems.h"
24 #include "libmesh/fe_interface.h"
25 #include "libmesh/libmesh.h"
26 #include "libmesh/system.h"
27 #include "libmesh/elem.h"
28 #include "libmesh/enum_elem_type.h"
29 #include "libmesh/int_range.h"
30 #include "libmesh/utility.h" // map_find
31 
32 // C++ includes
33 #include <sstream>
34 #include <fstream>
35 #include <string>
36 #include <iomanip>
37 
38 namespace libMesh
39 {
40 
41 // Initialize the static data members by calling the static build functions.
42 std::map<ElemType, std::string> EnsightIO::_element_map = EnsightIO::build_element_map();
43 
44 // Static function used to build the _element_map.
build_element_map()45 std::map<ElemType, std::string> EnsightIO::build_element_map()
46 {
47   std::map<ElemType, std::string> ret;
48   ret[EDGE2] = "bar2";
49   ret[EDGE3] = "bar3";
50   ret[QUAD4] = "quad4";
51   ret[QUAD8] = "quad8";
52   // ret[QUAD9] = "quad9"; // not supported
53   ret[TRI3] = "tria3";
54   ret[TRI6] = "tria6";
55   ret[TET4] = "tetra4";
56   ret[TET10] = "tetra10";
57   ret[HEX8] = "hexa8";
58   ret[HEX20] = "hexa20";
59   // ret[HEX27] = "HEX27"; // not supported
60   ret[PYRAMID5] = "pyramid5";
61   return ret;
62 }
63 
64 
EnsightIO(const std::string & filename,const EquationSystems & eq)65 EnsightIO::EnsightIO (const std::string & filename,
66                       const EquationSystems & eq) :
67   MeshOutput<MeshBase> (eq.get_mesh()),
68   _equation_systems(eq)
69 {
70   if (_equation_systems.n_processors() == 1)
71     _ensight_file_name = filename;
72   else
73     {
74       std::ostringstream tmp_file;
75       tmp_file << filename << "_rank" << _equation_systems.processor_id();
76       _ensight_file_name = tmp_file.str();
77     }
78 }
79 
80 
81 
add_vector(const std::string & system_name,const std::string & vec_description,const std::string & u,const std::string & v)82 void EnsightIO::add_vector (const std::string & system_name,
83                             const std::string & vec_description,
84                             const std::string & u,
85                             const std::string & v)
86 {
87   libmesh_assert (_equation_systems.has_system(system_name));
88   libmesh_assert (_equation_systems.get_system(system_name).has_variable(u));
89   libmesh_assert (_equation_systems.get_system(system_name).has_variable(v));
90 
91   Vectors vec;
92   vec.description = vec_description;
93   vec.components.push_back(u);
94   vec.components.push_back(v);
95 
96   _system_vars_map[system_name].EnsightVectors.push_back(vec);
97 }
98 
99 
100 
add_vector(const std::string & system_name,const std::string & vec_name,const std::string & u,const std::string & v,const std::string & w)101 void EnsightIO::add_vector (const std::string & system_name,
102                             const std::string & vec_name,
103                             const std::string & u,
104                             const std::string & v,
105                             const std::string & w)
106 {
107   libmesh_assert(_equation_systems.has_system(system_name));
108   libmesh_assert(_equation_systems.get_system(system_name).has_variable(u));
109   libmesh_assert(_equation_systems.get_system(system_name).has_variable(v));
110   libmesh_assert(_equation_systems.get_system(system_name).has_variable(w));
111 
112   Vectors vec;
113   vec.description = vec_name;
114   vec.components.push_back(u);
115   vec.components.push_back(v);
116   vec.components.push_back(w);
117   _system_vars_map[system_name].EnsightVectors.push_back(vec);
118 }
119 
120 
121 
add_scalar(const std::string & system_name,const std::string & scl_description,const std::string & s)122 void EnsightIO::add_scalar(const std::string & system_name,
123                            const std::string & scl_description,
124                            const std::string & s)
125 {
126   libmesh_assert(_equation_systems.has_system(system_name));
127   libmesh_assert(_equation_systems.get_system(system_name).has_variable(s));
128 
129   Scalars scl;
130   scl.description = scl_description;
131   scl.scalar_name = s;
132 
133   _system_vars_map[system_name].EnsightScalars.push_back(scl);
134 }
135 
136 
137 
138 // This method must be implemented as it is pure virtual in
139 // the MeshOutput base class.
write(const std::string & name)140 void EnsightIO::write (const std::string & name)
141 {
142   // We may need to gather a DistributedMesh to output it, making that
143   // const qualifier in our constructor a dirty lie
144   MeshSerializer serialize(const_cast<MeshBase &>(this->mesh()), !_is_parallel_format);
145 
146   _ensight_file_name = name;
147   this->write();
148 }
149 
150 
151 
write(Real time)152 void EnsightIO::write (Real time)
153 {
154   this->write_ascii(time);
155   this->write_case();
156 }
157 
158 
159 
write_ascii(Real time)160 void EnsightIO::write_ascii (Real time)
161 {
162   _time_steps.push_back(time);
163 
164   this->write_geometry_ascii();
165   this->write_solution_ascii();
166 }
167 
168 
169 
write_geometry_ascii()170 void EnsightIO::write_geometry_ascii()
171 {
172   std::ostringstream file;
173   file << _ensight_file_name
174        << ".geo"
175        << std::setw(3)
176        << std::setprecision(0)
177        << std::setfill('0')
178        << std::right
179        << _time_steps.size()-1;
180 
181   // Open a stream to write the mesh
182   std::ofstream mesh_stream(file.str().c_str());
183 
184   mesh_stream << "EnSight Gold Geometry File Format\n";
185   mesh_stream << "Generated by \n";
186   mesh_stream << "node id off\n";
187   mesh_stream << "element id given\n";
188   mesh_stream << "part\n";
189   mesh_stream << std::setw(10) << 1 << "\n";
190   mesh_stream << "uns-elements\n";
191   mesh_stream << "coordinates\n";
192 
193   // mapping between nodal index and your coordinates
194   std::map<int, Point> mesh_nodes_map;
195 
196   // Map for grouping elements of the same type
197   std::map<ElemType, std::vector<const Elem *>> ensight_parts_map;
198 
199   const MeshBase & the_mesh = MeshOutput<MeshBase>::mesh();
200 
201   // Construct the various required maps
202   for (const auto & elem : the_mesh.active_local_element_ptr_range())
203     {
204       ensight_parts_map[elem->type()].push_back(elem);
205 
206       for (const Node & node : elem->node_ref_range())
207         mesh_nodes_map[node.id()] = node;
208     }
209 
210   // Write number of local points
211   mesh_stream << std::setw(10) << mesh_nodes_map.size() << "\n";
212 
213   // write x, y, and z node positions, build mapping between
214   // ensight and libmesh node numbers.
215   std::map <int, int> ensight_node_index;
216   for (unsigned direction=0; direction<3; ++direction)
217     {
218       int i = 1;
219       for (const auto & pr : mesh_nodes_map)
220         {
221           mesh_stream << std::setw(12)
222                       << std::setprecision(5)
223                       << std::scientific
224                       << pr.second(direction)
225                       << "\n";
226           ensight_node_index[pr.first] = i++;
227         }
228     }
229 
230   // Write parts
231   for (const auto & pr : ensight_parts_map)
232     {
233       // Look up this ElemType in the map, error if not present.
234       std::string name = libmesh_map_find(_element_map, pr.first);
235 
236       // Write element type
237       mesh_stream << "\n" << name << "\n";
238 
239       const std::vector<const Elem *> & elem_ref = pr.second;
240 
241       // Write number of element
242       mesh_stream << std::setw(10) << elem_ref.size() << "\n";
243 
244       // Write element id
245       for (const auto & elem : elem_ref)
246         mesh_stream << std::setw(10) << elem->id() << "\n";
247 
248       // Write connectivity
249       for (auto i : index_range(elem_ref))
250         {
251           for (const auto & node : elem_ref[i]->node_ref_range())
252             {
253               // tests!
254               if (pr.first == QUAD9 && i==4)
255                 continue;
256 
257               // tests!
258               if (pr.first == HEX27 &&
259                   (i==4    || i ==10 || i == 12 ||
260                    i == 13 || i ==14 || i == 16 || i == 22))
261                 continue;
262 
263               mesh_stream << std::setw(10) << ensight_node_index[node.id()];
264             }
265           mesh_stream << "\n";
266         }
267     }
268 }
269 
270 
271 
272 
273 
write_case()274 void EnsightIO::write_case()
275 {
276   std::ostringstream case_file;
277   case_file << _ensight_file_name << ".case";
278 
279   // Open a stream for writing the case file.
280   std::ofstream case_stream(case_file.str().c_str());
281 
282   case_stream << "FORMAT\n";
283   case_stream << "type:  ensight gold\n\n";
284   case_stream << "GEOMETRY\n";
285   case_stream << "model:            1     " << _ensight_file_name << ".geo" << "***\n";
286 
287   // Write Variable per node section
288   if (!_system_vars_map.empty())
289     case_stream << "\n\nVARIABLE\n";
290 
291   for (const auto & pr : _system_vars_map)
292     {
293       for (const auto & scalar : pr.second.EnsightScalars)
294         case_stream << "scalar per node:   1  "
295                     << scalar.description << " "
296                     << _ensight_file_name << "_" << scalar.scalar_name << ".scl***\n";
297 
298       for (const auto & vec : pr.second.EnsightVectors)
299         case_stream << "vector per node:      1    "
300                     << vec.description << " "
301                     << _ensight_file_name << "_" << vec.description << ".vec***\n";
302 
303       // Write time step section
304       if (_time_steps.size() != 0)
305         {
306           case_stream << "\n\nTIME\n";
307           case_stream << "time set:             1\n";
308           case_stream << "number of steps:   " << std::setw(10) << _time_steps.size() << "\n";
309           case_stream << "filename start number:   " << std::setw(10) << 0 << "\n";
310           case_stream << "filename increment:  " << std::setw(10) << 1 << "\n";
311           case_stream << "time values:\n";
312           for (const auto & time : _time_steps)
313             case_stream << std::setw(12) << std::setprecision(5) << std::scientific << time << "\n";
314         }
315     }
316 }
317 
318 
319 // Write scalar and vector solution
write_solution_ascii()320 void EnsightIO::write_solution_ascii()
321 {
322   for (const auto & pr : _system_vars_map)
323     {
324       for (const auto & scalar : pr.second.EnsightScalars)
325         this->write_scalar_ascii(pr.first,
326                                  scalar.scalar_name);
327 
328       for (const auto & vec : pr.second.EnsightVectors)
329         this->write_vector_ascii(pr.first,
330                                  vec.components,
331                                  vec.description);
332     }
333 }
334 
335 
write_scalar_ascii(const std::string & sys,const std::string & var_name)336 void EnsightIO::write_scalar_ascii(const std::string & sys,
337                                    const std::string & var_name)
338 {
339   // Construct scalar variable filename
340   std::ostringstream scl_file;
341   scl_file << _ensight_file_name
342            << "_"
343            << var_name
344            << ".scl"
345            << std::setw(3)
346            << std::setprecision(0)
347            << std::setfill('0')
348            << std::right
349            << _time_steps.size()-1;
350 
351   // Open a stream and start writing scalar variable info.
352   std::ofstream scl_stream(scl_file.str().c_str());
353   scl_stream << "Per node scalar value\n";
354   scl_stream << "part\n";
355   scl_stream << std::setw(10) << 1 << "\n";
356   scl_stream << "coordinates\n";
357 
358   const MeshBase & the_mesh = MeshOutput<MeshBase>::mesh();
359   const unsigned int dim = the_mesh.mesh_dimension();
360   const System & system = _equation_systems.get_system(sys);
361   const DofMap & dof_map = system.get_dof_map();
362   int var = system.variable_number(var_name);
363 
364   std::vector<dof_id_type> dof_indices_scl;
365 
366   // Map from node id -> solution value.  We end up just writing this
367   // map out in order, not sure what would happen if there were holes
368   // in the numbering...
369   std::map<int, Real> local_soln;
370 
371   std::vector<Number> elem_soln;
372   std::vector<Number> nodal_soln;
373 
374   // Loop over active local elements, construct the nodal solution, and write it to file.
375   for (const auto & elem : the_mesh.active_local_element_ptr_range())
376     {
377       const FEType & fe_type = system.variable_type(var);
378 
379       dof_map.dof_indices (elem, dof_indices_scl, var);
380 
381       elem_soln.resize(dof_indices_scl.size());
382 
383       for (auto i : index_range(dof_indices_scl))
384         elem_soln[i] = system.current_solution(dof_indices_scl[i]);
385 
386       FEInterface::nodal_soln (dim, fe_type, elem, elem_soln, nodal_soln);
387 
388       libmesh_assert_equal_to (nodal_soln.size(), elem->n_nodes());
389 
390 #ifdef LIBMESH_USE_COMPLEX_NUMBERS
391       libmesh_error_msg("Complex-valued Ensight output not yet supported");
392 #endif
393 
394       for (auto n : elem->node_index_range())
395         local_soln[elem->node_id(n)] = libmesh_real(nodal_soln[n]);
396     }
397 
398   for (const auto & pr : local_soln)
399     scl_stream << std::setw(12)
400                << std::setprecision(5)
401                << std::scientific
402                << pr.second
403                << "\n";
404 }
405 
406 
write_vector_ascii(const std::string & sys,const std::vector<std::string> & vec,const std::string & var_name)407 void EnsightIO::write_vector_ascii(const std::string & sys,
408                                    const std::vector<std::string> & vec,
409                                    const std::string & var_name)
410 {
411   // Construct vector variable filename
412   std::ostringstream vec_file;
413   vec_file << _ensight_file_name
414            << "_"
415            << var_name
416            << ".vec"
417            << std::setw(3)
418            << std::setprecision(0)
419            << std::setfill('0')
420            << std::right
421            << _time_steps.size()-1;
422 
423   // Open a stream and start writing vector variable info.
424   std::ofstream vec_stream(vec_file.str().c_str());
425   vec_stream << "Per vector per value\n";
426   vec_stream << "part\n";
427   vec_stream << std::setw(10) << 1 << "\n";
428   vec_stream << "coordinates\n";
429 
430   // Get a constant reference to the mesh object.
431   const MeshBase & the_mesh = MeshOutput<MeshBase>::mesh();
432 
433   // The dimension that we are running
434   const unsigned int dim = the_mesh.mesh_dimension();
435 
436   const System & system = _equation_systems.get_system(sys);
437 
438   const DofMap & dof_map = system.get_dof_map();
439 
440   const unsigned int u_var = system.variable_number(vec[0]);
441   const unsigned int v_var = system.variable_number(vec[1]);
442   const unsigned int w_var = (dim==3) ? system.variable_number(vec[2]) : 0;
443 
444   std::vector<dof_id_type> dof_indices_u;
445   std::vector<dof_id_type> dof_indices_v;
446   std::vector<dof_id_type> dof_indices_w;
447 
448   // Map from node id -> solution value.  We end up just writing this
449   // map out in order, not sure what would happen if there were holes
450   // in the numbering...
451   std::map<int,std::vector<Real>> local_soln;
452 
453   // Now we will loop over all the elements in the mesh.
454   for (const auto & elem : the_mesh.active_local_element_ptr_range())
455     {
456       const FEType & fe_type = system.variable_type(u_var);
457 
458       dof_map.dof_indices (elem, dof_indices_u, u_var);
459       dof_map.dof_indices (elem, dof_indices_v, v_var);
460       if (dim==3)
461         dof_map.dof_indices (elem, dof_indices_w, w_var);
462 
463       std::vector<Number> elem_soln_u;
464       std::vector<Number> elem_soln_v;
465       std::vector<Number> elem_soln_w;
466 
467       std::vector<Number> nodal_soln_u;
468       std::vector<Number> nodal_soln_v;
469       std::vector<Number> nodal_soln_w;
470 
471       elem_soln_u.resize(dof_indices_u.size());
472       elem_soln_v.resize(dof_indices_v.size());
473       if (dim == 3)
474         elem_soln_w.resize(dof_indices_w.size());
475 
476       for (auto i : index_range(dof_indices_u))
477         {
478           elem_soln_u[i] = system.current_solution(dof_indices_u[i]);
479           elem_soln_v[i] = system.current_solution(dof_indices_v[i]);
480           if (dim==3)
481             elem_soln_w[i] = system.current_solution(dof_indices_w[i]);
482         }
483 
484       FEInterface::nodal_soln (dim, fe_type, elem, elem_soln_u, nodal_soln_u);
485       FEInterface::nodal_soln (dim, fe_type, elem, elem_soln_v, nodal_soln_v);
486       if (dim == 3)
487         FEInterface::nodal_soln (dim, fe_type, elem, elem_soln_w, nodal_soln_w);
488 
489       libmesh_assert_equal_to (nodal_soln_u.size(), elem->n_nodes());
490       libmesh_assert_equal_to (nodal_soln_v.size(), elem->n_nodes());
491 
492 #ifdef LIBMESH_ENABLE_COMPLEX
493       libmesh_error_msg("Complex-valued Ensight output not yet supported");
494 #endif
495 
496       for (const auto & n : elem->node_index_range())
497         {
498           std::vector<Real> node_vec(3);
499           node_vec[0] = libmesh_real(nodal_soln_u[n]);
500           node_vec[1] = libmesh_real(nodal_soln_v[n]);
501           node_vec[2] = 0.0;
502           if (dim==3)
503             node_vec[2] = libmesh_real(nodal_soln_w[n]);
504           local_soln[elem->node_id(n)] = node_vec;
505         }
506     }
507 
508   for (unsigned dir=0; dir<3; ++dir)
509     {
510       for (const auto & pr : local_soln)
511         vec_stream << std::setw(12)
512                    << std::scientific
513                    << std::setprecision(5)
514                    << pr.second[dir]
515                    << "\n";
516     }
517 }
518 
519 } // namespace libMesh
520