1 /*=========================================================================
2
3 Program: Visualization Toolkit
4 Module: vtkVASPTessellationReader.cxx
5
6 Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
7 All rights reserved.
8 See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
9
10 This software is distributed WITHOUT ANY WARRANTY; without even
11 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12 PURPOSE. See the above copyright notice for more information.
13
14 =========================================================================*/
15
16 #include "vtkVASPTessellationReader.h"
17
18 #include "vtkBoundingBox.h"
19 #include "vtkCellData.h"
20 #include "vtkCellType.h"
21 #include "vtkDataSetAttributes.h"
22 #include "vtkFloatArray.h"
23 #include "vtkIdTypeArray.h"
24 #include "vtkInformation.h"
25 #include "vtkInformationVector.h"
26 #include "vtkMolecule.h"
27 #include "vtkNew.h"
28 #include "vtkObjectFactory.h"
29 #include "vtkPointLocator.h"
30 #include "vtkPoints.h"
31 #include "vtkStreamingDemandDrivenPipeline.h"
32 #include "vtkUnsignedShortArray.h"
33 #include "vtkUnstructuredGrid.h"
34 #include "vtkVector.h"
35 #include "vtkVectorOperators.h"
36
37 #include <vtksys/RegularExpression.hxx>
38
39 #include <algorithm>
40 #include <cassert>
41 #include <cmath>
42 #include <fstream>
43 #include <set>
44 #include <sstream>
45 #include <vector>
46
47 vtkStandardNewMacro(vtkVASPTessellationReader)
48
49 typedef vtksys::RegularExpression RegEx;
50 typedef vtkStreamingDemandDrivenPipeline vtkSDDP;
51
52 namespace {
53
54 template <typename T>
parse(const std::string & str,T & result)55 bool parse(const std::string &str, T &result)
56 {
57 if (!str.empty())
58 {
59 std::istringstream tmp(str);
60 tmp >> result;
61 return !tmp.fail();
62 }
63 return false;
64 }
65
66 template <typename T>
parseCommaSepList(const std::string & input,std::vector<T> & data)67 bool parseCommaSepList(const std::string &input, std::vector<T> &data)
68 {
69 std::istringstream in(input);
70 for (std::string valStr; std::getline(in, valStr, ',');)
71 {
72 T tmp;
73 if (!parse(valStr, tmp))
74 {
75 return false;
76 }
77 data.push_back(tmp);
78 }
79 return true;
80 }
81
82 // This parses the voronoi points/faces list. The input is expected to be:
83 // [number of commaSepLists], ([commaSepList]) ([commaSepList]) ...
84 template <typename T>
parseVariableLists(const std::string & input,std::vector<std::vector<T>> & data,RegEx * parenExtract)85 bool parseVariableLists(const std::string &input,
86 std::vector<std::vector<T> > &data,
87 RegEx *parenExtract)
88 {
89 std::istringstream in(input);
90 size_t nLists;
91 in >> nLists;
92 if (in.fail())
93 {
94 return false;
95 }
96
97 data.resize(nLists);
98
99 std::string parseBuffer = input;
100 for (size_t i = 0; i < nLists; ++i)
101 {
102 if (!parenExtract->find(parseBuffer))
103 {
104 return false;
105 }
106 if (!parseCommaSepList(parenExtract->match(1), data[i]))
107 {
108 return false;
109 }
110 // Chop the current match off of the buffer to prepare for the next iter.
111 parseBuffer = parseBuffer.substr(parenExtract->end());
112 }
113
114 return true;
115 }
116
117 } // end anon namespace
118
119 //------------------------------------------------------------------------------
PrintSelf(std::ostream & os,vtkIndent indent)120 void vtkVASPTessellationReader::PrintSelf(std::ostream &os, vtkIndent indent)
121 {
122 this->Superclass::PrintSelf(os, indent);
123 }
124
125 //------------------------------------------------------------------------------
vtkVASPTessellationReader()126 vtkVASPTessellationReader::vtkVASPTessellationReader()
127 : FileName(nullptr),
128 TimeParser(new RegEx("^ *time *= *([0-9EeDd.+-]+) *$")), // time = (timeVal)
129 LatticeParser(new RegEx("^ *Rx1 *= *([0-9EeDd.+-]+) *," // Rx1
130 " *Rx2 *= *([0-9EeDd.+-]+) *," // Rx2
131 " *Rx3 *= *([0-9EeDd.+-]+) *," // Rx3
132 " *Ry2 *= *([0-9EeDd.+-]+) *," // Ry2
133 " *Ry3 *= *([0-9EeDd.+-]+) *," // Ry3
134 " *Rz3 *= *([0-9EeDd.+-]+) *$" // Rz3
135 )),
136 AtomCountParser(new RegEx("^ *Natoms *= *([0-9]+) *$")), // Natoms = (int)),
137 AtomParser(new RegEx("^ *([0-9]+) *," // Atom index
138 " *\\(" // Open paren
139 " *([0-9EeDd.+-]+) *," // X coord
140 " *([0-9EeDd.+-]+) *," // Y coord
141 " *([0-9EeDd.+-]+)" // Z coord
142 " *\\) *," // Close paren
143 " *([0-9EeDd.+-]+) *$" // Radius
144 )),
145 ParenExtract(new RegEx("\\(([^(]+)\\)")) // Extract contents of (...)
146 {
147 this->SetNumberOfInputPorts(0);
148 this->SetNumberOfOutputPorts(2);
149 }
150
151 //------------------------------------------------------------------------------
~vtkVASPTessellationReader()152 vtkVASPTessellationReader::~vtkVASPTessellationReader()
153 {
154 this->SetFileName(nullptr);
155 delete this->TimeParser;
156 delete this->LatticeParser;
157 delete this->AtomCountParser;
158 delete this->AtomParser;
159 delete this->ParenExtract;
160 }
161
162 //------------------------------------------------------------------------------
RequestData(vtkInformation *,vtkInformationVector **,vtkInformationVector * outInfos)163 int vtkVASPTessellationReader::RequestData(vtkInformation *,
164 vtkInformationVector **,
165 vtkInformationVector *outInfos)
166 {
167 vtkInformation *outInfo0 = outInfos->GetInformationObject(0);
168 vtkInformation *outInfo1 = outInfos->GetInformationObject(1);
169
170 vtkMolecule *molecule = vtkMolecule::SafeDownCast(
171 outInfo0->Get(vtkDataObject::DATA_OBJECT()));
172 assert(molecule);
173
174 vtkUnstructuredGrid *voronoi = vtkUnstructuredGrid::SafeDownCast(
175 outInfo1->Get(vtkDataObject::DATA_OBJECT()));
176 assert(voronoi);
177
178 std::ifstream in(this->FileName);
179 if (!in)
180 {
181 vtkErrorMacro("Could not open file for reading: "
182 << (this->FileName ? this->FileName : ""));
183 return 1;
184 }
185
186 // Advance to the selected timestep:
187 size_t stepIdx = this->SelectTimeStepIndex(outInfo0);
188 double time = 0.;
189 for (size_t i = 0; i <= stepIdx; ++i) // <= to read the "time=" line
190 {
191 if (!this->NextTimeStep(in, time))
192 {
193 vtkErrorMacro("Error -- attempting to read timestep #" << (stepIdx + 1)
194 << " but encountered a parsing error at timestep #"
195 << (i + 1) << ".");
196 return 1;
197 }
198 }
199
200 if (this->ReadTimeStep(in, molecule, voronoi))
201 {
202 molecule->GetInformation()->Set(vtkDataObject::DATA_TIME_STEP(), time);
203 voronoi->GetInformation()->Set(vtkDataObject::DATA_TIME_STEP(), time);
204 }
205 else
206 {
207 molecule->Initialize();
208 voronoi->Initialize();
209 }
210
211 return 1;
212 }
213
214 //------------------------------------------------------------------------------
RequestInformation(vtkInformation *,vtkInformationVector **,vtkInformationVector * outInfos)215 int vtkVASPTessellationReader::RequestInformation(
216 vtkInformation *, vtkInformationVector **,
217 vtkInformationVector *outInfos)
218 {
219 std::ifstream in(this->FileName);
220 if (!in)
221 {
222 vtkErrorMacro("Could not open file for reading: "
223 << (this->FileName ? this->FileName : ""));
224 return 1;
225 }
226
227 // Scan the file for timesteps:
228 double time;
229 std::vector<double> times;
230 double timeRange[2] = { VTK_DOUBLE_MAX, VTK_DOUBLE_MIN };
231 while (this->NextTimeStep(in, time))
232 {
233 times.push_back(time);
234 timeRange[0] = std::min(timeRange[0], time);
235 timeRange[1] = std::max(timeRange[1], time);
236 }
237
238 if (!times.empty())
239 {
240 for (int port = 0; port < 2; ++port)
241 {
242 vtkInformation *outInfo = outInfos->GetInformationObject(port);
243 outInfo->Set(vtkSDDP::TIME_RANGE(), timeRange, 2);
244 outInfo->Set(vtkSDDP::TIME_STEPS(), ×[0],
245 static_cast<int>(times.size()));
246 }
247 }
248
249 return 1;
250 }
251
252 //------------------------------------------------------------------------------
FillOutputPortInformation(int port,vtkInformation * info)253 int vtkVASPTessellationReader::FillOutputPortInformation(int port,
254 vtkInformation *info)
255 {
256 switch (port)
257 {
258 case 0:
259 info->Set(vtkDataObject::DATA_TYPE_NAME(), "vtkMolecule");
260 break;
261 case 1:
262 info->Set(vtkDataObject::DATA_TYPE_NAME(), "vtkUnstructuredGrid");
263 break;
264 default:
265 return 0;
266 }
267
268 return 1;
269 }
270
271 //------------------------------------------------------------------------------
NextTimeStep(std::istream & in,double & time)272 bool vtkVASPTessellationReader::NextTimeStep(std::istream &in, double &time)
273 {
274 std::string line;
275 while (std::getline(in, line))
276 {
277 if (this->TimeParser->find(line))
278 {
279 // Parse timestamp:
280 if (!parse(this->TimeParser->match(1), time))
281 {
282 vtkErrorMacro("Error parsing time information from line: " << line);
283 return false;
284 }
285 return true;
286 }
287 }
288
289 return false;
290 }
291
292 //------------------------------------------------------------------------------
SelectTimeStepIndex(vtkInformation * info)293 size_t vtkVASPTessellationReader::SelectTimeStepIndex(vtkInformation *info)
294 {
295 if (!info->Has(vtkSDDP::TIME_STEPS()) ||
296 !info->Has(vtkSDDP::UPDATE_TIME_STEP()))
297 {
298 return 0;
299 }
300
301 double *times = info->Get(vtkSDDP::TIME_STEPS());
302 int nTimes = info->Length(vtkSDDP::TIME_STEPS());
303 double t = info->Get(vtkSDDP::UPDATE_TIME_STEP());
304
305 double resultDiff = VTK_DOUBLE_MAX;
306 size_t result = 0;
307 for (int i = 0; i < nTimes; ++i)
308 {
309 double diff = std::fabs(times[i] - t);
310 if (diff < resultDiff)
311 {
312 resultDiff = diff;
313 result = static_cast<size_t>(i);
314 }
315 }
316
317 return result;
318 }
319
320 //------------------------------------------------------------------------------
ReadTimeStep(std::istream & in,vtkMolecule * molecule,vtkUnstructuredGrid * voronoi)321 bool vtkVASPTessellationReader::ReadTimeStep(std::istream &in,
322 vtkMolecule *molecule,
323 vtkUnstructuredGrid *voronoi)
324 {
325 // Assume the 'time = ...' line has already been read.
326 std::string line;
327
328 // Read the lattice info:
329 if (!std::getline(in, line))
330 {
331 vtkErrorMacro("Unexpected EOF while reading lattice info.");
332 return false;
333 }
334 if (!this->LatticeParser->find(line))
335 {
336 vtkErrorMacro("Error parsing lattice info from line: " << line);
337 return false;
338 }
339
340 vtkVector3d latA(0.);
341 vtkVector3d latB(0.);
342 vtkVector3d latC(0.);
343 vtkVector3d latO(0.);
344
345 // Rxx
346 if (!parse(this->LatticeParser->match(1), latA[0]))
347 {
348 vtkErrorMacro("Error parsing Rxx component '"
349 << this->LatticeParser->match(1) << "' from line: " << line);
350 return false;
351 }
352 // Rxy
353 if (!parse(this->LatticeParser->match(2), latB[0]))
354 {
355 vtkErrorMacro("Error parsing Rxy component '"
356 << this->LatticeParser->match(2) << "' from line: " << line);
357 return false;
358 }
359 // Rxz
360 if (!parse(this->LatticeParser->match(3), latC[0]))
361 {
362 vtkErrorMacro("Error parsing Rxz component '"
363 << this->LatticeParser->match(3) << "' from line: " << line);
364 return false;
365 }
366 // Ryy
367 if (!parse(this->LatticeParser->match(4), latB[1]))
368 {
369 vtkErrorMacro("Error parsing Ryy component '"
370 << this->LatticeParser->match(4) << "' from line: " << line);
371 return false;
372 }
373 // Ryz
374 if (!parse(this->LatticeParser->match(5), latC[1]))
375 {
376 vtkErrorMacro("Error parsing Ryz component '"
377 << this->LatticeParser->match(5) << "' from line: " << line);
378 return false;
379 }
380 // Rzz
381 if (!parse(this->LatticeParser->match(6), latC[2]))
382 {
383 vtkErrorMacro("Error parsing Rzz component '"
384 << this->LatticeParser->match(6) << "' from line: " << line);
385 return false;
386 }
387
388 molecule->SetLattice(latA, latB, latC);
389 molecule->SetLatticeOrigin(latO);
390
391 // Number of atoms:
392 if (!std::getline(in, line))
393 {
394 vtkErrorMacro("Unexpected EOF while parsing number of atoms.");
395 return false;
396 }
397 if (!this->AtomCountParser->find(line))
398 {
399 vtkErrorMacro("Error parsing number of atoms from line: " << line);
400 return false;
401 }
402 vtkIdType nAtoms;
403 if (!parse(this->AtomCountParser->match(1), nAtoms))
404 {
405 vtkErrorMacro("Error parsing number atoms '"
406 << this->AtomCountParser->match(1) << "'' from line: "
407 << line);
408 return false;
409 }
410
411 // Skip 'Atomic_Numbers =' line:
412 if (!std::getline(in, line))
413 {
414 vtkErrorMacro("Unexpected EOF.");
415 return false;
416 }
417
418 // Atomic numbers:
419 std::vector<unsigned short> atomicNumbers;
420 atomicNumbers.reserve(static_cast<size_t>(nAtoms));
421 if (!std::getline(in, line))
422 {
423 vtkErrorMacro("Unexpected EOF while reading atomic number list.");
424 return false;
425 }
426 if (!parseCommaSepList(line, atomicNumbers))
427 {
428 vtkErrorMacro("Error while parsing atomic number list: " << line);
429 return false;
430 }
431
432 // Initialize the molecule with atoms, setting just the atomic number.
433 // We'll add positions as we parse them later.
434 if (static_cast<size_t>(nAtoms) != atomicNumbers.size())
435 {
436 vtkErrorMacro("Error: expected " << nAtoms << " atomic numbers, but only "
437 "parsed " << atomicNumbers.size());
438 return false;
439 }
440 vtkVector3f pos(0.f);
441 for (size_t i = 0; i < atomicNumbers.size(); ++i)
442 {
443 molecule->AppendAtom(atomicNumbers[i], pos);
444 }
445
446 // Now for the actual list of atoms/tessellations
447 vtkNew<vtkFloatArray> radii;
448 radii->SetNumberOfTuples(nAtoms);
449
450 // Compute unit cell bounds to initialize point merging:
451 vtkBoundingBox bbox;
452 bbox.AddPoint(latO.GetData());
453 bbox.AddPoint((latO + latA).GetData());
454 bbox.AddPoint((latO + latB).GetData());
455 bbox.AddPoint((latO + latC).GetData());
456 bbox.AddPoint((latO + latA + latB).GetData());
457 bbox.AddPoint((latO + latA + latC).GetData());
458 bbox.AddPoint((latO + latB + latC).GetData());
459 bbox.AddPoint((latO + latA + latB + latC).GetData());
460 double bounds[6];
461 bbox.GetBounds(bounds);
462
463 // Merge the tessellation points using a locator:
464 vtkNew<vtkPointLocator> locator;
465 vtkNew<vtkPoints> tessPoints;
466 tessPoints->SetDataTypeToFloat();
467 voronoi->SetPoints(tessPoints);
468 voronoi->Allocate(nAtoms);
469
470 // Cell attributes for the voronoi tessellation:
471 vtkNew<vtkUnsignedShortArray> tessAtomicNumbers;
472 tessAtomicNumbers->SetName("Atomic Numbers");
473 tessAtomicNumbers->Allocate(nAtoms);
474 vtkNew<vtkIdTypeArray> tessAtomIds;
475 tessAtomIds->SetName("Atom Ids");
476 tessAtomIds->Allocate(nAtoms);
477
478 // Estimate 10 unique points per atom:
479 locator->InitPointInsertion(tessPoints, bounds, nAtoms * 10);
480
481 // Storage for parsing the tessellation points/faces info
482 std::vector<vtkIdType> faceStream;
483 std::vector<vtkIdType> pointIds;
484 std::set<vtkIdType> uniquePointIds;
485 // parse as doubles for locator API, but store as floats:
486 std::vector<std::vector<double> > pointData;
487 std::vector<std::vector<vtkIdType> > faceData;
488
489 for (vtkIdType atomEntry = 0; atomEntry < nAtoms; ++atomEntry)
490 {
491 // Skip any blank lines:
492 line.clear();
493 while (line.empty() || line.find_first_not_of(" \t") == std::string::npos)
494 {
495 if (!std::getline(in, line))
496 {
497 vtkErrorMacro("Unexpected EOF while reading atom entry " << atomEntry);
498 return false;
499 }
500 }
501
502 if (!this->AtomParser->find(line))
503 {
504 vtkErrorMacro("Error parsing atom position/radius specification: "
505 << line);
506 return false;
507 }
508 vtkIdType atomId;
509 if (!parse(this->AtomParser->match(1), atomId))
510 {
511 vtkErrorMacro("Error parsing atomId '" << this->AtomParser->match(1)
512 << "' from line: " << line);
513 return false;
514 }
515 if (!parse(this->AtomParser->match(2), pos[0]))
516 {
517 vtkErrorMacro("Error parsing x coordinate '" << this->AtomParser->match(2)
518 << "' from line: " << line);
519 return false;
520 }
521 if (!parse(this->AtomParser->match(3), pos[1]))
522 {
523 vtkErrorMacro("Error parsing y coordinate '" << this->AtomParser->match(3)
524 << "' from line: " << line);
525 return false;
526 }
527 if (!parse(this->AtomParser->match(4), pos[2]))
528 {
529 vtkErrorMacro("Error parsing z coordinate '" << this->AtomParser->match(4)
530 << "' from line: " << line);
531 return false;
532 }
533 float radius;
534 if (!parse(this->AtomParser->match(5), radius))
535 {
536 vtkErrorMacro("Error parsing radius '" << this->AtomParser->match(5)
537 << "' from line: " << line);
538 return false;
539 }
540
541 if (atomId >= nAtoms)
542 {
543 vtkErrorMacro("Found entry for atom with id " << atomId << ", but "
544 "only " << nAtoms << " atoms exist.");
545 return false;
546 }
547 vtkAtom atom = molecule->GetAtom(atomId);
548 atom.SetPosition(pos);
549 radii->SetTypedComponent(atomId, 0, radius);
550
551 // Extract tessellation points:
552 pointData.clear();
553 if (!std::getline(in, line))
554 {
555 vtkErrorMacro("Unexpected EOF while reading voronoi points for atom "
556 << atomId);
557 return false;
558 }
559 if (!parseVariableLists(line, pointData, this->ParenExtract))
560 {
561 vtkErrorMacro("Error while parsing voronoi point data for atom "
562 << atomId << ". Input: " << line);
563 return false;
564 }
565
566 // Extract tessellation faces:
567 faceData.clear();
568 if (!std::getline(in, line))
569 {
570 vtkErrorMacro("Unexpected EOF while reading voronoi faces for atom "
571 << atomId);
572 return false;
573 }
574 if (!parseVariableLists(line, faceData, this->ParenExtract))
575 {
576 vtkErrorMacro("Error while parsing voronoi face data for atom "
577 << atomId << ". Input: " << line);
578 return false;
579 }
580
581 // Add points to locator:
582 pointIds.resize(pointData.size());
583 uniquePointIds.clear();
584 for (size_t i = 0; i < pointData.size(); ++i)
585 {
586 const std::vector<double> &p = pointData[i];
587 if (p.size() != 3)
588 {
589 vtkErrorMacro("Error: Tessellation point " << i << " for atom "
590 << atomId << " has " << p.size() << " components. "
591 "Expected a 3D coordinate.");
592 return false;
593 }
594 locator->InsertUniquePoint(&p[0], pointIds[i]);
595 uniquePointIds.insert(pointIds[i]);
596 }
597
598 // Create face stream:
599 faceStream.clear();
600 for (size_t faceId = 0; faceId < faceData.size(); ++faceId)
601 {
602 const std::vector<vtkIdType> &face = faceData[faceId];
603 faceStream.push_back(static_cast<vtkIdType>(face.size()));
604 for (std::vector<vtkIdType>::const_iterator it = face.begin(),
605 itEnd = face.end(); it != itEnd; ++it)
606 {
607 // Convert the local point id into the dataset point id:
608 vtkIdType datasetId = pointIds[*it];
609 faceStream.push_back(datasetId);
610 }
611 }
612
613 // Reuse pointIds to prepare a contiguous buffer of the unique pointId set
614 pointIds.resize(uniquePointIds.size());
615 std::copy(uniquePointIds.begin(), uniquePointIds.end(), pointIds.begin());
616
617 // Add cell to tessellation dataset:
618 voronoi->InsertNextCell(VTK_POLYHEDRON,
619 static_cast<vtkIdType>(pointIds.size()),
620 pointIds.empty() ? nullptr : &pointIds[0],
621 static_cast<vtkIdType>(faceData.size()),
622 faceStream.empty() ? nullptr : &faceStream[0]);
623 tessAtomicNumbers->InsertNextValue(atom.GetAtomicNumber());
624 tessAtomIds->InsertNextValue(atom.GetId());
625 }
626
627 molecule->GetVertexData()->AddArray(radii);
628 voronoi->GetCellData()->SetScalars(tessAtomicNumbers);
629 voronoi->GetCellData()->AddArray(tessAtomIds);
630
631 return true;
632 }
633