1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573
|
// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-FileCopyrightText: Copyright (c) Sandia Corporation
// SPDX-License-Identifier: BSD-3-Clause
#include "vtkIOSSReader.h" // For enums
#include "vtkIOSSUtilities.h" // For enums
#include <vtk_ioss.h>
// clang-format off
#include VTK_IOSS(Ionit_Initializer.h)
#include VTK_IOSS(Ioss_Assembly.h)
#include VTK_IOSS(Ioss_DatabaseIO.h)
#include VTK_IOSS(Ioss_EdgeBlock.h)
#include VTK_IOSS(Ioss_EdgeSet.h)
#include VTK_IOSS(Ioss_ElementBlock.h)
#include VTK_IOSS(Ioss_ElementSet.h)
#include VTK_IOSS(Ioss_ElementTopology.h)
#include VTK_IOSS(Ioss_FaceBlock.h)
#include VTK_IOSS(Ioss_FaceSet.h)
#include VTK_IOSS(Ioss_IOFactory.h)
#include VTK_IOSS(Ioss_NodeBlock.h)
#include VTK_IOSS(Ioss_NodeSet.h)
#include VTK_IOSS(Ioss_Region.h)
#include VTK_IOSS(Ioss_SideBlock.h)
#include VTK_IOSS(Ioss_SideSet.h)
#include VTK_IOSS(Ioss_StructuredBlock.h)
// clang-format on
#include <algorithm>
#include <array>
#include <map>
#include <set>
#include <string>
#include <vector>
VTK_ABI_NAMESPACE_BEGIN
class vtkCellData;
class vtkDataAssembly;
class vtkDataSetAttributes;
class vtkFieldData;
class vtkIdTypeArray;
class vtkPartitionedDataSetCollection;
class vtkPointData;
class vtkPointSet;
class vtkStructuredGrid;
class vtkUnsignedCharArray;
class vtkUnstructuredGrid;
struct DatabasePartitionInfo
{
int ProcessCount = 0;
std::set<int> Ranks;
bool operator==(const DatabasePartitionInfo& other) const
{
return this->ProcessCount == other.ProcessCount && this->Ranks == other.Ranks;
}
};
// Opaque handle used to identify a specific Region
using DatabaseHandle = std::pair<std::string, int>;
/**
* @class vtkIOSSReaderInternal
* @brief Internal methods and state for the IOSS reader.
*
* Note that this class is not part of the public API of VTK and thus
* has no export macros. It has been put in a separate file so that a
* subclass of the reader local to this module (vtkIOSSCellGridReader)
* can access it and so it can be subclassed.
*/
class vtkIOSSReaderInternal
{
protected:
// It's okay to instantiate this multiple times.
Ioss::Init::Initializer io;
double DisplacementMagnitude = 1.;
using DatabaseNamesType = std::map<std::string, DatabasePartitionInfo>;
DatabaseNamesType UnfilteredDatabaseNames;
DatabaseNamesType DatabaseNames;
vtkTimeStamp DatabaseNamesMTime;
std::map<std::string, std::vector<std::pair<int, double>>> DatabaseTimes;
std::vector<double> TimestepValues;
vtkTimeStamp TimestepValuesMTime;
// a collection of names for blocks and sets in the file(s).
std::array<std::set<vtkIOSSUtilities::EntityNameType>, vtkIOSSReader::NUMBER_OF_ENTITY_TYPES>
EntityNames;
vtkTimeStamp SelectionsMTime;
// Keeps track of idx of a partitioned dataset in the output.
std::map<std::pair<Ioss::EntityType, std::string>, unsigned int> DatasetIndexMap;
std::map<DatabaseHandle, std::shared_ptr<Ioss::Region>> RegionMap;
vtkIOSSUtilities::Cache Cache;
vtkIOSSUtilities::DatabaseFormatType Format = vtkIOSSUtilities::DatabaseFormatType::UNKNOWN;
vtkIOSSReader* IOSSReader = nullptr;
vtkSmartPointer<vtkDataAssembly> Assembly;
vtkTimeStamp AssemblyMTime;
public:
using EntityType = vtkIOSSReader::EntityType;
vtkIOSSReaderInternal(vtkIOSSReader* reader)
: IOSSReader(reader)
{
}
virtual ~vtkIOSSReaderInternal() = default; // Force polymorphism
Ioss::PropertyManager DatabaseProperties;
std::set<std::string> FileNames;
vtkTimeStamp FileNamesMTime;
std::set<std::string> Selectors;
const std::vector<double>& GetTimeSteps() const { return this->TimestepValues; }
vtkIOSSUtilities::DatabaseFormatType GetFormat() const { return this->Format; }
void SetDisplacementMagnitude(double s) { this->DisplacementMagnitude = s; }
double GetDisplacementMagnitude() { return this->DisplacementMagnitude; }
///@{
/**
* Cache related API.
*/
void ClearCache() { this->Cache.Clear(); }
void ResetCacheAccessCounts() { this->Cache.ResetAccessCounts(); }
void ClearCacheUnused() { this->Cache.ClearUnused(); }
///@}
/**
* Processes filenames to populate names for Ioss databases to read.
*
* A file collection representing files partitioned across ranks where each
* rank generate a separate file (spatial partitioning) are all represented
* by a single Ioss database.
*
* Multiple Ioss databases are generated when the files are a temporal
* in nature or represent restarts.
*
* This method simply uses the filenames to determine what type of files we
* are encountering. For spatial partitions, the filenames must end with
* '{processor-count}.{rank}'.
*
* @returns `false` to indicate failure.
*/
bool UpdateDatabaseNames(vtkIOSSReader* self);
/**
* Read Ioss databases to generate information about timesteps / times
* in the databases.
*
* This is called after successful call to `UpdateDatabaseNames` which should
* populate the list of Ioss databases. This method iterates over all
* databases and gathers information about timesteps available in those
* databases. When running in parallel, only the root node opens the Ioss
* databases and reads the time information. That information is then
* exchanged with all ranks thus at the end of this method all ranks should
* have their time information updated.
*
* @returns `false` on failure.
*/
bool UpdateTimeInformation(vtkIOSSReader* self);
/**
* Checks if the entity and field selections have changed.
*/
bool NeedToUpdateEntityAndFieldSelections(
vtkIOSSReader* self, const std::vector<DatabaseHandle>& dbaseHandles);
/**
* Populates various `vtkDataArraySelection` objects on the vtkIOSSReader with
* names for entity-blocks, -sets, and fields defined on them.
*/
bool UpdateEntityAndFieldSelections(vtkIOSSReader* self);
/**
* Populates the vtkDataAssembly used for block/set selection.
*/
bool UpdateAssembly(vtkIOSSReader* self, int* tag);
vtkDataAssembly* GetAssembly() const;
/**
* Fills up the output data-structure based on the entity blocks/sets chosen
* and those available.
*/
bool GenerateOutput(vtkPartitionedDataSetCollection* output, vtkIOSSReader* self);
/**
* Fills up the vtkDataAssembly with ioss-assemblies, if present.
*/
bool ReadAssemblies(vtkPartitionedDataSetCollection* output, const DatabaseHandle& handle);
/**
* Reads datasets (meshes and fields) for the given block.
*/
std::vector<vtkSmartPointer<vtkDataSet>> GetDataSets(const std::string& blockname,
vtkIOSSReader::EntityType vtk_entity_type, const DatabaseHandle& handle, int timestep,
vtkIOSSReader* self);
/**
* Reads datasets (meshes and fields) for the given exodus entity.
*
* This method is only invoked when MergeExodusEntityBlocks is true
* (which is not the default).
*/
vtkSmartPointer<vtkDataSet> GetExodusEntityDataSet(const std::vector<std::string>& blockNames,
vtkIOSSReader::EntityType vtk_entity_type, const DatabaseHandle& handle, int timestep,
vtkIOSSReader* self);
/**
* Read quality assurance and information data from the file.
*/
bool GetQAAndInformationRecords(vtkFieldData* fd, const DatabaseHandle& handle);
/**
* Read global fields.
*/
bool GetGlobalFields(vtkFieldData* fd, const DatabaseHandle& handle, int timestep);
/**
* Get if there are restart files available.
*/
bool HaveRestartFiles() const { return this->DatabaseTimes.size() > 1; }
/**
* Returns the list of fileids, if any to be read for a given "piece" for the
* chosen timestep.
*/
std::vector<DatabaseHandle> GetDatabaseHandles(int piece, int npieces, int timestep) const;
/**
* Useful for printing error messages etc.
*/
std::string GetRawFileName(const DatabaseHandle& handle, bool shortname = false) const
{
auto iter = this->DatabaseNames.find(handle.first);
if (iter == this->DatabaseNames.end())
{
throw std::runtime_error("bad database handle!");
}
const int& fileid = handle.second;
auto dbasename = shortname ? vtksys::SystemTools::GetFilenameName(handle.first) : handle.first;
auto& dinfo = iter->second;
if (dinfo.ProcessCount > 0)
{
return Ioss::Utils::decode_filename(
dbasename, dinfo.ProcessCount, *std::next(dinfo.Ranks.begin(), fileid));
}
return dbasename;
}
/**
* For spatially partitioned files, this returns the partition identifier for
* the file identified by the handle.
*/
int GetFileProcessor(const DatabaseHandle& handle) const
{
auto iter = this->DatabaseNames.find(handle.first);
if (iter == this->DatabaseNames.end())
{
throw std::runtime_error("bad database handle!");
}
const int& fileid = handle.second;
auto& dinfo = iter->second;
if (dinfo.ProcessCount > 0)
{
return *std::next(dinfo.Ranks.begin(), fileid);
}
// this is not a spatially partitioned file; just return 0.
return 0;
}
/**
* Returns if the given database handles have regions already created.
*/
bool HaveCreatedRegions(const std::vector<DatabaseHandle>& dbaseHandles)
{
if (this->RegionMap.empty())
{
return false;
}
const bool allHandlesAreNew = std::all_of(dbaseHandles.begin(), dbaseHandles.end(),
[&](const DatabaseHandle& handle)
{ return this->RegionMap.find(handle) == this->RegionMap.end(); });
return !allHandlesAreNew;
}
/**
* Releases any open file handles.
*/
void ReleaseHandles()
{
// RegionMap is where all the handles are kept. All we need to do is release
// them.
for (const auto& pair : this->RegionMap)
{
pair.second->get_database()->closeDatabase();
}
}
/**
* Little more aggressive than `ReleaseHandles` but less intense than `Reset`,
* releases all IOSS regions and thus all the meta-data IOSS may have cached
* as well.
*/
void ReleaseRegions() { this->RegionMap.clear(); }
/**
* Clear all regions, databases etc.
*/
void Reset()
{
this->Cache.Clear();
this->RegionMap.clear();
this->DatabaseNames.clear();
this->IOSSReader->RemoveAllSelections();
this->DatabaseNamesMTime = vtkTimeStamp();
this->SelectionsMTime = vtkTimeStamp();
this->TimestepValuesMTime = vtkTimeStamp();
}
void ResetDatabaseNamesMTime() { this->DatabaseNamesMTime = vtkTimeStamp(); }
protected:
std::vector<int> GetFileIds(const std::string& dbasename, int myrank, int numRanks) const;
Ioss::Region* GetRegion(const std::string& dbasename, int fileid);
Ioss::Region* GetRegion(const DatabaseHandle& handle)
{
return this->GetRegion(handle.first, handle.second);
}
///@{
/**
* Reads a field with name `fieldname` from entity block or set with chosen name
* (`blockname`) and type (`vtk_entity_type`). Field may be a result
* field which can be time-varying. In that case, `timestep` is used to
* identify the timestep to read.
*
* Returns non-null array on success. Returns nullptr if block or field is
* missing (which is not an error condition).
*
* On error, `std::runtime_error` is thrown.
*/
vtkSmartPointer<vtkAbstractArray> GetField(const std::string& fieldname, Ioss::Region* region,
const Ioss::GroupingEntity* group_entity, const DatabaseHandle& handle, int timestep,
vtkIdTypeArray* ids_to_extract = nullptr, const std::string& cache_key_suffix = std::string());
///@}
/**
* Get a vector of cell arrays and their cell type for the entity block (or set) with the
* given name (`blockname`) and type (vtk_entity_type).
*
* `handle` is the database / file handle for the current piece / rank
* obtained by calling `GetDatabaseHandles`.
*
* Returns a vector of cell arrays and their cell type.
*
* On file reading error, `std::runtime_error` is thrown.
*/
std::vector<std::pair<int, vtkSmartPointer<vtkCellArray>>> GetTopology(
const std::string& blockname, vtkIOSSReader::EntityType vtk_entity_type,
const DatabaseHandle& handle);
/**
* Combine a vector cell types, cell arrays pairs into a single
* vtkUnsignedCharArray of cell types and a vtkCellArray.
*/
std::pair<vtkSmartPointer<vtkUnsignedCharArray>, vtkSmartPointer<vtkCellArray>> CombineTopologies(
const std::vector<std::pair<int, vtkSmartPointer<vtkCellArray>>>& topologies);
/**
* Fill up the `grid` with connectivity information for the entity block (or
* set) with the given name (`blockname`) and type (vtk_entity_type).
*
* `handle` is the database / file handle for the current piece / rank
* obtained by calling `GetDatabaseHandles`.
*
* Returns true on success. `false` will be returned when the handle doesn't
* have the chosen blockname/entity.
*
* On file reading error, `std::runtime_error` is thrown.
*/
bool GetTopology(vtkUnstructuredGrid* grid, const std::string& blockname,
vtkIOSSReader::EntityType vtk_entity_type, const DatabaseHandle& handle);
/**
* Get with point coordinates aka geometry read from the block
* with the given name (`blockname`). The point coordinates are always
* read from a block of type NODEBLOCK.
*
* `handle` is the database / file handle for the current piece / rank
* obtained by calling `GetDatabaseHandles`.
*
* Returns points on success.
*
* On file reading error, `std::runtime_error` is thrown.
*/
vtkSmartPointer<vtkPoints> GetGeometry(
const std::string& blockname, const DatabaseHandle& handle);
/**
* Fill up `grid` with point coordinates aka geometry read from the block
* with the given name (`blockname`). The point coordinates are always
* read from a block of type NODEBLOCK.
*
* `handle` is the database / file handle for the current piece / rank
* obtained by calling `GetDatabaseHandles`.
*
* Returns true on success.
*
* On file reading error, `std::runtime_error` is thrown.
*/
bool GetGeometry(
vtkUnstructuredGrid* grid, const std::string& blockname, const DatabaseHandle& handle);
/**
* GetGeometry for vtkStructuredGrid i.e. CGNS.
*/
bool GetGeometry(vtkStructuredGrid* grid, const Ioss::StructuredBlock* groupEntity);
/**
* Adds geometry (points) and topology (cell) information to the grid for the
* entity block or set chosen using the name (`blockname`) and type
* (`vtk_entity_type`).
*
* `handle` is the database / file handle for the current piece / rank
* obtained by calling `GetDatabaseHandles`.
*
* If `remove_unused_points` is true, any points that are not used by the
* cells are removed. When that is done, an array called
* `__vtk_mesh_original_pt_ids__` is added to the cache for the entity
* which can be used to identify which points were passed through.
*
* This method is only invoked when MergeExodusEntityBlocks is false
* (which is the default).
*/
bool GetMesh(vtkUnstructuredGrid* grid, const std::string& blockname,
vtkIOSSReader::EntityType vtk_entity_type, const DatabaseHandle& handle,
bool remove_unused_points);
/**
* Adds geometry (points) and topology (cell) information to the grid for all the
* entity blocks or sets chosen using the names (`blockNames`) and type
* (`vtk_entity_type`).
*
* `handle` is the database / file handle for the current piece / rank
* obtained by calling `GetDatabaseHandles`.
*
* This method is only invoked when MergeExodusEntityBlocks is true
* (which is not the default).
*/
bool GetEntityMesh(vtkUnstructuredGrid* grid, const std::vector<std::string>& blockNames,
vtkIOSSReader::EntityType vtk_entity_type, const DatabaseHandle& handle);
/**
* Reads a structured block. vtk_entity_type must be
* `vtkIOSSReader::STRUCTUREDBLOCK`.
*
* This method is only invoked when MergeExodusEntityBlocks is false
* (which is the default).
*/
bool GetMesh(vtkStructuredGrid* grid, const std::string& blockname,
vtkIOSSReader::EntityType vtk_entity_type, const DatabaseHandle& handle);
/**
* Add "id" array to the dataset using the id for the grouping entity, if
* any. The array named "object_id" is added as a cell-data array to follow
* the pattern used by vtkExodusIIReader.
*/
bool GenerateEntityIdArray(vtkCellData* cd, vtkIdType numberOfCells, const std::string& blockname,
vtkIOSSReader::EntityType vtk_entity_type, const DatabaseHandle& handle);
/**
* Reads selected field arrays for the given entity block or set.
* If `read_ioss_ids` is true, then element ids are read as applicable.
*
* `ids_to_extract`, when specified, is a `vtkIdTypeArray` identifying the
* subset of indices to produce in the output. This is used for point data fields
* when the mesh was generated with `remove_unused_points` on. This ensures
* that point data arrays match the points. When `ids_to_extract` is provided,
* for the caching to work correctly, the `cache_key_suffix` must be set to
* the name of the entity block (or set) which provided the cells to determine
* which points to extract.
*
* Returns true on success.
*
* On error, `std::runtime_error` is thrown.
*/
bool GetFields(vtkDataSetAttributes* dsa, vtkDataArraySelection* selection, Ioss::Region* region,
Ioss::GroupingEntity* group_entity, const DatabaseHandle& handle, int timestep,
bool read_ioss_ids, vtkIdTypeArray* ids_to_extract = nullptr,
const std::string& cache_key_suffix = std::string());
/**
* This reads node fields for an entity block or set.
*
* Internally calls `GetFields()` with correct values for `ids_to_extract` and
* `cache_key_suffix`.
*
*/
bool GetNodeFields(vtkDataSetAttributes* dsa, vtkDataArraySelection* selection,
Ioss::Region* region, Ioss::GroupingEntity* group_entity, const DatabaseHandle& handle,
int timestep, bool read_ioss_ids, bool mergeExodusEntityBlocks = false);
/**
* Reads node block array with displacements and then transforms
* the points in the grid using those displacements.
*/
bool ApplyDisplacements(vtkPointSet* grid, Ioss::Region* region,
Ioss::GroupingEntity* group_entity, const DatabaseHandle& handle, int timestep,
bool mergeExodusEntityBlocks = false);
/**
* Adds 'file_id' array to indicate which file the dataset was read from.
*/
bool GenerateFileId(vtkDataSetAttributes* cellData, vtkIdType numberOfCells,
Ioss::GroupingEntity* group_entity, const DatabaseHandle& handle);
/**
* Fields like "ids" have to be vtkIdTypeArray in VTK. This method does the
* conversion if needed.
*/
vtkSmartPointer<vtkAbstractArray> ConvertFieldForVTK(vtkAbstractArray* array);
unsigned int GetDataSetIndexForEntity(const Ioss::GroupingEntity* entity) const
{
return this->DatasetIndexMap.at(std::make_pair(entity->type(), entity->name()));
}
/// Add field-data arrays holding side-set specifications
/// (i.e., (cell-id, side-id) tuples) for use by the
/// UnstructuredGridToCellGrid conversion filter.
void GenerateElementAndSideIds(vtkDataSet* dataset, Ioss::SideSet* sideSet,
const DatabaseHandle& handle, const std::string& blockname,
vtkIOSSReader::EntityType vtk_entity_type);
///@{
/**
* Called by `GetDataSets` to process each type of dataset.
* There's slight difference in how they are handled and hence these separate methods.
*/
std::vector<vtkSmartPointer<vtkDataSet>> GetExodusDataSets(const std::string& blockname,
vtkIOSSReader::EntityType vtk_entity_type, const DatabaseHandle& handle, int timestep,
vtkIOSSReader* self);
std::vector<vtkSmartPointer<vtkDataSet>> GetCGNSDataSets(const std::string& blockname,
vtkIOSSReader::EntityType vtk_entity_type, const DatabaseHandle& handle, int timestep,
vtkIOSSReader* self);
///@}
bool BuildAssembly(Ioss::Region* region, vtkDataAssembly* assembly, int root, bool add_leaves);
/**
* Generate a subset based the readers current settings for FileRange and
* FileStride.
*/
DatabaseNamesType GenerateSubset(const DatabaseNamesType& databases, vtkIOSSReader* self);
};
VTK_ABI_NAMESPACE_END
|