1 /**
2 * MOAB, a Mesh-Oriented datABase, is a software component for creating,
3 * storing and accessing finite element mesh data.
4 *
5 * Copyright 2004 Sandia Corporation. Under the terms of Contract
6 * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government
7 * retains certain rights in this software.
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 */
15
16 #ifdef WIN32
17 #ifdef _DEBUG
18 // turn off warnings that say they debugging identifier has been truncated
19 // this warning comes up when using some STL containers
20 #pragma warning(disable : 4786)
21 #endif
22 #endif
23
24 #include "WriteVtk.hpp"
25 #include "moab/VtkUtil.hpp"
26 #include "SysUtil.hpp"
27
28 #include <fstream>
29 #include <iostream>
30 #include <stdio.h>
31 #include <assert.h>
32 #include <vector>
33 #include <set>
34 #include <map>
35 #include <iterator>
36
37 #include "moab/Interface.hpp"
38 #include "moab/Range.hpp"
39 #include "moab/CN.hpp"
40 #include "MBTagConventions.hpp"
41 #include "moab/WriteUtilIface.hpp"
42 #include "Internals.hpp"
43 #include "moab/FileOptions.hpp"
44
45 #define INS_ID(stringvar, prefix, id) \
46 sprintf(stringvar, prefix, id)
47
48 namespace moab {
49
50 const int DEFAULT_PRECISION = 10;
51 const bool DEFAULT_STRICT = true;
52
factory(Interface * iface)53 WriterIface *WriteVtk::factory(Interface* iface)
54 {
55 return new WriteVtk(iface);
56 }
57
WriteVtk(Interface * impl)58 WriteVtk::WriteVtk(Interface* impl)
59 : mbImpl(impl), writeTool(0), mStrict(DEFAULT_STRICT), freeNodes(0), createOneNodeCells(false)
60 {
61 assert(impl != NULL);
62 impl->query_interface(writeTool);
63 }
64
~WriteVtk()65 WriteVtk::~WriteVtk()
66 {
67 mbImpl->release_interface(writeTool);
68 }
69
write_file(const char * file_name,const bool overwrite,const FileOptions & opts,const EntityHandle * output_list,const int num_sets,const std::vector<std::string> &,const Tag * tag_list,int num_tags,int)70 ErrorCode WriteVtk::write_file(const char *file_name,
71 const bool overwrite,
72 const FileOptions& opts,
73 const EntityHandle *output_list,
74 const int num_sets,
75 const std::vector<std::string>& /* qa_list */,
76 const Tag* tag_list,
77 int num_tags,
78 int /* export_dimension */)
79 {
80 ErrorCode rval;
81
82 // Get precision for node coordinates
83 int precision;
84 if (MB_SUCCESS != opts.get_int_option("PRECISION", precision))
85 precision = DEFAULT_PRECISION;
86
87 if (MB_SUCCESS == opts.get_null_option("STRICT"))
88 mStrict = true;
89 else if (MB_SUCCESS == opts.get_null_option("RELAXED"))
90 mStrict = false;
91 else
92 mStrict = DEFAULT_STRICT;
93
94 if (MB_SUCCESS == opts.get_null_option("CREATE_ONE_NODE_CELLS"))
95 createOneNodeCells = true;
96
97 // Get entities to write
98 Range nodes, elems;
99 rval = gather_mesh(output_list, num_sets, nodes, elems);
100 if (MB_SUCCESS != rval)
101 return rval;
102
103 // Honor overwrite flag
104 if (!overwrite) {
105 rval = writeTool->check_doesnt_exist(file_name);
106 if (MB_SUCCESS != rval)
107 return rval;
108 }
109
110 // Create file
111 std::ofstream file(file_name);
112 if (!file) {
113 MB_SET_ERR(MB_FILE_WRITE_ERROR, "Could not open file: " << file_name);
114 }
115 file.precision(precision);
116
117 // Write file
118 if ((rval = write_header(file )) != MB_SUCCESS ||
119 (rval = write_nodes( file, nodes )) != MB_SUCCESS ||
120 (rval = write_elems( file, nodes, elems)) != MB_SUCCESS ||
121 (rval = write_tags ( file, true, nodes, tag_list, num_tags)) != MB_SUCCESS ||
122 (rval = write_tags ( file, false, elems, tag_list, num_tags)) != MB_SUCCESS) {
123 file.close();
124 remove(file_name);
125 return rval;
126 }
127
128 return MB_SUCCESS;
129 }
130
gather_mesh(const EntityHandle * set_list,int num_sets,Range & nodes,Range & elems)131 ErrorCode WriteVtk::gather_mesh(const EntityHandle* set_list,
132 int num_sets,
133 Range& nodes,
134 Range& elems)
135 {
136 ErrorCode rval;
137 int e;
138
139 if (!set_list || !num_sets) {
140 Range a;
141 rval = mbImpl->get_entities_by_handle(0, a);
142 if (MB_SUCCESS != rval)
143 return rval;
144
145 Range::const_iterator node_i, elem_i, set_i;
146 node_i = a.lower_bound(a.begin(), a.end(), CREATE_HANDLE( MBVERTEX, 0, e));
147 elem_i = a.lower_bound( node_i, a.end(), CREATE_HANDLE( MBEDGE, 0, e));
148 set_i = a.lower_bound( elem_i, a.end(), CREATE_HANDLE(MBENTITYSET, 0, e));
149 nodes.merge(node_i, elem_i);
150 elems.merge(elem_i, set_i);
151
152 // Filter out unsupported element types
153 EntityType et = MBEDGE;
154 for (et++; et < MBENTITYSET; et++) {
155 if (VtkUtil::get_vtk_type(et, CN::VerticesPerEntity(et)))
156 continue;
157 Range::iterator
158 eit = elems.lower_bound(elems.begin(), elems.end(), CREATE_HANDLE(et, 0, e)),
159 ep1it = elems.lower_bound(elems.begin(), elems.end(), CREATE_HANDLE(et + 1, 0, e));
160 elems.erase(eit, ep1it);
161 }
162 }
163 else {
164 std::set<EntityHandle> visited;
165 std::vector<EntityHandle> sets;
166 sets.reserve(num_sets);
167 std::copy(set_list, set_list + num_sets, std::back_inserter(sets));
168 while (!sets.empty()) {
169 // Get next set
170 EntityHandle set = sets.back();
171 sets.pop_back();
172 // Skip sets we've already done
173 if (!visited.insert(set).second)
174 continue;
175
176 Range a;
177 rval = mbImpl->get_entities_by_handle(set, a);
178 if (MB_SUCCESS != rval)
179 return rval;
180
181 Range::const_iterator node_i, elem_i, set_i;
182 node_i = a.lower_bound(a.begin(), a.end(), CREATE_HANDLE( MBVERTEX, 0, e));
183 elem_i = a.lower_bound( node_i, a.end(), CREATE_HANDLE( MBEDGE, 0, e));
184 set_i = a.lower_bound( elem_i, a.end(), CREATE_HANDLE(MBENTITYSET, 0, e));
185 nodes.merge(node_i, elem_i);
186 elems.merge(elem_i, set_i);
187 std::copy(set_i, a.end(), std::back_inserter(sets));
188
189 a.clear();
190 rval = mbImpl->get_child_meshsets(set, a);
191 std::copy(a.begin(), a.end(), std::back_inserter(sets));
192 }
193
194 for (Range::const_iterator ei = elems.begin(); ei != elems.end(); ++ei) {
195 std::vector<EntityHandle> connect;
196 rval = mbImpl->get_connectivity(&(*ei), 1, connect);
197 if (MB_SUCCESS != rval)
198 return rval;
199
200 for (unsigned int i = 0; i < connect.size(); ++i)
201 nodes.insert(connect[i]);
202 }
203 }
204
205 if (nodes.empty()) {
206 MB_SET_ERR(MB_ENTITY_NOT_FOUND, "Nothing to write");
207 }
208
209 return MB_SUCCESS;
210 }
211
write_header(std::ostream & stream)212 ErrorCode WriteVtk::write_header(std::ostream& stream)
213 {
214 stream << "# vtk DataFile Version 3.0" << std::endl;
215 stream << MOAB_VERSION_STRING << std::endl;
216 stream << "ASCII" << std::endl;
217 stream << "DATASET UNSTRUCTURED_GRID" << std::endl;
218 return MB_SUCCESS;
219 }
220
write_nodes(std::ostream & stream,const Range & nodes)221 ErrorCode WriteVtk::write_nodes(std::ostream& stream, const Range& nodes)
222 {
223 ErrorCode rval;
224
225 stream << "POINTS " << nodes.size() << " double" << std::endl;
226
227 double coords[3];
228 for (Range::const_iterator i = nodes.begin(); i != nodes.end(); ++i) {
229 coords[1] = coords[2] = 0.0;
230 rval = mbImpl->get_coords(&(*i), 1, coords);
231 if (MB_SUCCESS != rval)
232 return rval;
233 stream << coords[0] << ' ' << coords[1] << ' ' << coords[2] << std::endl;
234 }
235
236 return MB_SUCCESS;
237 }
238
write_elems(std::ostream & stream,const Range & nodes,const Range & elems)239 ErrorCode WriteVtk::write_elems(std::ostream& stream,
240 const Range& nodes,
241 const Range& elems)
242 {
243 ErrorCode rval;
244
245
246 Range connectivity; // because we now support polyhedra, it could contain faces
247 rval = mbImpl->get_connectivity(elems, connectivity); MB_CHK_ERR(rval);
248
249 Range nodes_from_connectivity = connectivity.subset_by_type(MBVERTEX);
250 Range faces_from_connectivity = subtract(connectivity, nodes_from_connectivity); // these could be faces of polyhedra
251
252 Range connected_nodes;
253 rval = mbImpl->get_connectivity(faces_from_connectivity, connected_nodes); MB_CHK_ERR(rval);
254 connected_nodes.merge(nodes_from_connectivity);
255
256 Range free_nodes = subtract(nodes, connected_nodes);
257
258 // Get and write counts
259 unsigned long num_elems, num_uses;
260 num_elems = num_uses = elems.size();
261
262 std::map<EntityHandle, int> sizeFieldsPolyhedra;
263
264 for (Range::const_iterator i = elems.begin(); i != elems.end(); ++i) {
265 EntityType type = mbImpl->type_from_handle(*i);
266 if (!VtkUtil::get_vtk_type(type, CN::VerticesPerEntity(type)))
267 continue;
268
269
270 EntityHandle elem=*i;
271 const EntityHandle * connect=NULL;
272 int conn_len=0;
273 rval = mbImpl->get_connectivity(elem, connect, conn_len);MB_CHK_ERR(rval);
274
275 num_uses += conn_len;
276 // if polyhedra, we will count the number of nodes in each face too
277 if ( TYPE_FROM_HANDLE(elem) == MBPOLYHEDRON)
278 {
279 int numFields = 1; // there will be one for number of faces; forgot about this one
280 for (int j=0; j<conn_len; j++)
281 {
282 const EntityHandle * conn = NULL;
283 int num_nd=0;
284 rval = mbImpl->get_connectivity(connect[j], conn, num_nd);MB_CHK_ERR(rval);
285 numFields += num_nd +1;
286 }
287 sizeFieldsPolyhedra[elem] = numFields; // will be used later, at writing
288 num_uses += (numFields-conn_len);
289 }
290 }
291 freeNodes = (int)free_nodes.size();
292 if (!createOneNodeCells)
293 freeNodes=0; // do not create one node cells
294 stream << "CELLS " << num_elems + freeNodes<< ' ' << num_uses + 2*freeNodes << std::endl;
295
296 // Write element connectivity
297 std::vector<int> conn_data;
298 std::vector<unsigned> vtk_types(elems.size() + freeNodes );
299 std::vector<unsigned>::iterator t = vtk_types.begin();
300 for (Range::const_iterator i = elems.begin(); i != elems.end(); ++i) {
301 // Get type information for element
302 EntityHandle elem = *i;
303 EntityType type = TYPE_FROM_HANDLE(elem);
304
305 // Get element connectivity
306 const EntityHandle * connect = NULL;
307 int conn_len = 0;
308 rval = mbImpl->get_connectivity(elem, connect, conn_len); MB_CHK_ERR(rval);
309
310 // Get VTK type
311 const VtkElemType* vtk_type = VtkUtil::get_vtk_type(type, conn_len);
312 if (!vtk_type) {
313 // Try connectivity with 1 fewer node
314 vtk_type = VtkUtil::get_vtk_type(type, conn_len - 1);
315 if (vtk_type)
316 conn_len--;
317 else {
318 MB_SET_ERR(MB_FAILURE, "Vtk file format does not support elements of type " << CN::EntityTypeName(type) << " (" << (int)type << ") with " << conn_len << " nodes");
319 }
320 }
321
322 // Save VTK type index for later
323 *t = vtk_type->vtk_type;
324 ++t;
325
326 if (type!=MBPOLYHEDRON)
327 {
328 // Get IDs from vertex handles
329 assert(conn_len > 0);
330 conn_data.resize(conn_len);
331 for (int j = 0; j < conn_len; ++j)
332 conn_data[j] = nodes.index(connect[j]);
333
334 // Write connectivity list
335 stream << conn_len;
336 if (vtk_type->node_order)
337 for (int k = 0; k < conn_len; ++k)
338 stream << ' ' << conn_data[vtk_type->node_order[k]];
339 else
340 for (int k = 0; k < conn_len; ++k)
341 stream << ' ' << conn_data[k];
342 stream << std::endl;
343 }
344 else
345 {
346 // POLYHEDRON needs a special case, loop over faces to get nodes
347 stream << sizeFieldsPolyhedra[elem] << " " << conn_len;
348 for (int k=0; k<conn_len; k++)
349 {
350 EntityHandle face=connect[k];
351 const EntityHandle * conn = NULL;
352 int num_nodes=0;
353 rval = mbImpl->get_connectivity(face, conn, num_nodes);MB_CHK_ERR(rval);
354 // num_uses += num_nd + 1; // 1 for number of vertices in face
355 conn_data.resize(num_nodes);
356 for (int j = 0; j < num_nodes; ++j)
357 conn_data[j] = nodes.index(conn[j]);
358
359 stream << ' ' << num_nodes;
360
361 for (int j = 0; j < num_nodes; ++j)
362 stream << ' ' << conn_data[j];
363 }
364 stream << std::endl;
365
366 }
367 }
368
369 if (createOneNodeCells)
370 for (Range::const_iterator v=free_nodes.begin(); v!= free_nodes.end(); ++v, ++t)
371 {
372 EntityHandle node=*v;
373 stream << "1 " << nodes.index(node) << std::endl;
374 *t = 1;
375 }
376
377 // Write element types
378 stream << "CELL_TYPES " << vtk_types.size() << std::endl;
379 for (std::vector<unsigned>::const_iterator i = vtk_types.begin(); i != vtk_types.end(); ++i)
380 stream << *i << std::endl;
381
382 return MB_SUCCESS;
383 }
384
write_tags(std::ostream & stream,bool nodes,const Range & entities,const Tag * tag_list,int num_tags)385 ErrorCode WriteVtk::write_tags(std::ostream& stream,
386 bool nodes,
387 const Range& entities,
388 const Tag* tag_list,
389 int num_tags)
390 {
391 ErrorCode rval;
392
393 // The #$%@#$% MOAB interface does not have a function to retrieve
394 // all entities with a tag, only all entities with a specified type
395 // and tag. Define types to loop over depending on the if vertex
396 // or element tag data is being written. It seems horribly inefficient
397 // to have the implementation subdivide the results by type and have
398 // to call that function once for each type just to recombine the results.
399 // Unfortunately, there doesn't seem to be any other way.
400 EntityType low_type, high_type;
401 if (nodes) {
402 low_type = MBVERTEX;
403 high_type = MBEDGE;
404 }
405 else {
406 low_type = MBEDGE;
407 high_type = MBENTITYSET;
408 }
409
410 // Get all defined tags
411 std::vector<Tag> tags;
412 std::vector<Tag>::iterator i;
413 rval = writeTool->get_tag_list(tags, tag_list, num_tags, false);
414 if (MB_SUCCESS != rval)
415 return rval;
416
417 // For each tag...
418 bool entities_have_tags = false;
419 for (i = tags.begin(); i != tags.end(); ++i) {
420 // Skip tags holding entity handles -- no way to save them
421 DataType dtype;
422 rval = mbImpl->tag_get_data_type(*i, dtype);
423 if (MB_SUCCESS != rval)
424 return rval;
425 if (dtype == MB_TYPE_HANDLE)
426 continue;
427
428 // If in strict mode, don't write tags that do not fit in any
429 // attribute type (SCALAR : 1 to 4 values, VECTOR : 3 values, TENSOR : 9 values)
430 if (mStrict) {
431 int count;
432 rval = mbImpl->tag_get_length(*i, count);
433 if (MB_SUCCESS != rval)
434 return rval;
435 if (count < 1 || (count > 4 && count != 9))
436 continue;
437 }
438
439 // Get subset of input entities that have the tag set
440 Range tagged;
441 for (EntityType type = low_type; type < high_type; ++type) {
442 Range tmp_tagged;
443 rval = mbImpl->get_entities_by_type_and_tag(0, type, &(*i), 0, 1, tmp_tagged);
444 if (MB_SUCCESS != rval)
445 return rval;
446 tmp_tagged = intersect(tmp_tagged, entities);
447 tagged.merge(tmp_tagged);
448 }
449
450 // If any entities were tagged
451 if (!tagged.empty()) {
452 // If this is the first tag being written for the
453 // entity type, write the label marking the beginning
454 // of the tag data.
455 if (!entities_have_tags) {
456 entities_have_tags = true;
457 if (nodes)
458 stream << "POINT_DATA " << entities.size() << std::endl;
459 else
460 stream << "CELL_DATA " << entities.size() + freeNodes << std::endl;
461 }
462
463 // Write the tag
464 rval = write_tag(stream, *i, entities, tagged);
465 if (MB_SUCCESS != rval)
466 return rval;
467 }
468 }
469
470 return MB_SUCCESS;
471 }
472
473 template <typename T>
write_data(std::ostream & stream,const std::vector<T> & data,unsigned vals_per_tag)474 void WriteVtk::write_data(std::ostream& stream,
475 const std::vector<T>& data,
476 unsigned vals_per_tag)
477 {
478 typename std::vector<T>::const_iterator d = data.begin();
479 const unsigned long n = data.size() / vals_per_tag;
480
481 for (unsigned long i = 0; i < n; ++i) {
482 for (unsigned j = 0; j < vals_per_tag; ++j, ++d) {
483 if (sizeof(T) == 1)
484 stream << (unsigned int)*d << ' ';
485 else
486 stream << *d << ' ';
487 }
488 stream << std::endl;
489 }
490 }
491
492 //template <>
493 //void WriteVtk::write_data<unsigned char>(std::ostream& stream,
494 // const std::vector<unsigned char>& data,
495 // unsigned vals_per_tag)
496 //{
497 // std::vector<unsigned char>::const_iterator d = data.begin();
498 // const unsigned long n = data.size() / vals_per_tag;
499 //
500 // for (unsigned long i = 0; i < n; ++i) {
501 // for (unsigned j = 0; j < vals_per_tag; ++j, ++d)
502 // stream << (unsigned int)*d << ' ';
503 // stream << std::endl;
504 // }
505 //}
506
507 template <typename T>
write_tag(std::ostream & stream,Tag tag,const Range & entities,const Range & tagged,const int)508 ErrorCode WriteVtk::write_tag(std::ostream& stream,
509 Tag tag,
510 const Range& entities,
511 const Range& tagged,
512 const int)
513 {
514 ErrorCode rval;
515 int addFreeNodes = 0;
516 if (TYPE_FROM_HANDLE(entities[0])>MBVERTEX)
517 addFreeNodes = freeNodes;
518 // we created freeNodes 1-node cells, so we have to augment cell data too
519 // we know that the 1 node cells are added at the end, after all other cells;
520 // so the default values will be set to those extra , artificial cells
521 const unsigned long n = entities.size() + addFreeNodes;
522
523 // Get tag properties
524
525 std::string name;
526 int vals_per_tag;
527 if (MB_SUCCESS != mbImpl->tag_get_name(tag, name) ||
528 MB_SUCCESS != mbImpl->tag_get_length(tag, vals_per_tag))
529 return MB_FAILURE;
530
531 // Get a tag value for each entity. Do this by initializing the
532 // "data" vector with zero, and then filling in the values for
533 // the entities that actually have the tag set.
534 std::vector<T> data;
535 data.resize(n * vals_per_tag, 0);
536 // If there is a default value for the tag, set the actual default value
537 std::vector<T> def_value(vals_per_tag);
538 rval = mbImpl->tag_get_default_value(tag, &(def_value[0]));
539 if (MB_SUCCESS == rval)
540 SysUtil::setmem(&(data[0]), &(def_value[0]), vals_per_tag * sizeof(T), n);
541
542 Range::const_iterator t = tagged.begin();
543 typename std::vector<T>::iterator d = data.begin();
544 for (Range::const_iterator i = entities.begin();
545 i != entities.end() && t != tagged.end(); ++i, d += vals_per_tag) {
546 if (*i == *t) {
547 ++t;
548 rval = mbImpl->tag_get_data(tag, &(*i), 1, &(*d));
549 if (MB_SUCCESS != rval)
550 return rval;
551 }
552 }
553
554 // Write the tag values, one entity per line.
555 write_data(stream, data, vals_per_tag);
556
557 return MB_SUCCESS;
558 }
559
write_bit_tag(std::ostream & stream,Tag tag,const Range & entities,const Range & tagged)560 ErrorCode WriteVtk::write_bit_tag(std::ostream& stream,
561 Tag tag,
562 const Range& entities,
563 const Range& tagged)
564 {
565 ErrorCode rval;
566 const unsigned long n = entities.size();
567
568 // Get tag properties
569
570 std::string name;
571 int vals_per_tag;
572 if (MB_SUCCESS != mbImpl->tag_get_name(tag, name) ||
573 MB_SUCCESS != mbImpl->tag_get_length(tag, vals_per_tag))
574 return MB_FAILURE;
575
576 if (vals_per_tag > 8) {
577 MB_SET_ERR(MB_FAILURE, "Invalid tag size for bit tag \"" << name << "\"");
578 }
579
580 // Get a tag value for each entity.
581 // Get bits for each entity and unpack into
582 // one integer in the 'data' array for each bit.
583 // Initialize 'data' to zero because we will skip
584 // those entities for which the tag is not set.
585 std::vector<unsigned short> data;
586 data.resize(n * vals_per_tag, 0);
587 Range::const_iterator t = tagged.begin();
588 std::vector<unsigned short>::iterator d = data.begin();
589 for (Range::const_iterator i = entities.begin();
590 i != entities.end() && t != tagged.end(); ++i) {
591 if (*i == *t) {
592 ++t;
593 unsigned char value;
594 rval = mbImpl->tag_get_data(tag, &(*i), 1, &value);
595 for (int j = 0; j < vals_per_tag; ++j, ++d)
596 *d = (unsigned short)(value & (1 << j) ? 1 : 0);
597 if (MB_SUCCESS != rval)
598 return rval;
599 }
600 else {
601 // If tag is not set for entity, skip values in array
602 d += vals_per_tag;
603 }
604 }
605
606 // Write the tag values, one entity per line.
607 write_data(stream, data, vals_per_tag);
608
609 return MB_SUCCESS;
610 }
611
write_tag(std::ostream & s,Tag tag,const Range & entities,const Range & tagged)612 ErrorCode WriteVtk::write_tag(std::ostream& s, Tag tag,
613 const Range& entities,
614 const Range& tagged)
615 {
616 // Get tag properties
617 std::string name;
618 DataType type;
619 int vals_per_tag;
620 if (MB_SUCCESS != mbImpl->tag_get_name(tag, name) ||
621 MB_SUCCESS != mbImpl->tag_get_length(tag, vals_per_tag) ||
622 MB_SUCCESS != mbImpl->tag_get_data_type(tag, type))
623 return MB_FAILURE;
624
625 // Skip tags of type ENTITY_HANDLE
626 if (MB_TYPE_HANDLE == type)
627 return MB_FAILURE;
628
629 // Now that we're past the point where the name would be used in
630 // an error message, remove any spaces to conform to VTK file.
631 for (std::string::iterator i = name.begin(); i != name.end(); ++i) {
632 if (isspace(*i) || iscntrl(*i))
633 *i = '_';
634 }
635
636 // Write the tag description
637 if (3 == vals_per_tag && MB_TYPE_DOUBLE == type)
638 s << "VECTORS " << name << ' ' << VtkUtil::vtkTypeNames[type] << std::endl;
639 else if (9 == vals_per_tag)
640 s << "TENSORS " << name << ' ' << VtkUtil::vtkTypeNames[type] << std::endl;
641 else
642 s << "SCALARS " << name << ' ' << VtkUtil::vtkTypeNames[type] << ' '
643 << vals_per_tag << std::endl << "LOOKUP_TABLE default" << std::endl;
644
645 // Write the tag data
646 switch (type) {
647 case MB_TYPE_OPAQUE:
648 return write_tag<unsigned char>(s, tag, entities, tagged, 0);
649 case MB_TYPE_INTEGER:
650 return write_tag<int>(s, tag, entities, tagged, 0);
651 case MB_TYPE_DOUBLE:
652 return write_tag<double>(s, tag, entities, tagged, 0);
653 case MB_TYPE_BIT:
654 return write_bit_tag(s, tag, entities, tagged);
655 default:
656 return MB_FAILURE;
657 }
658 }
659
660 } // namespace moab
661