// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen // SPDX-License-Identifier: BSD-3-Clause #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /** * This tests the core features of the FDS reader, i.e. the parsing of: * - Grid * - Devices & HRR * - Slices * - Boundaries * * When adding new features, please consider updating the associated data * (edit the test_core.fds file and re-run the simulation) instead of adding * new files when possible. */ namespace { template bool testValue(T1 gotVal, T2 expectedVal, const char* valName) { if (gotVal != expectedVal) { std::cerr << "Wrong " << valName << ". Expected " << expectedVal << ", got " << gotVal << std::endl; return false; } return true; } template bool testValueFuzzy(T1 gotVal, T2 expectedVal, const char* valName) { if (!vtkMathUtilities::FuzzyCompare(gotVal, expectedVal)) { std::cerr << "Wrong " << valName << ". Expected " << expectedVal << ", got " << gotVal << std::endl; return false; } return true; } } bool TestEmptyFile(const std::string& tempDirectory) { // Create empty file. std::string emptyFilePath = tempDirectory + "/empty.smv"; std::ofstream outputStream(emptyFilePath); outputStream.close(); vtkNew reader; reader->SetFileName(emptyFilePath); auto previousGlobalWarningFlag = vtkObject::GetGlobalWarningDisplay(); vtkObject::SetGlobalWarningDisplay(0); reader->UpdateTimeStep(0.0); vtkObject::SetGlobalWarningDisplay(previousGlobalWarningFlag); return true; } bool TestExampleFile(const std::string& dataRoot) { // Test RequestInformation vtkNew reader; std::string fileName = dataRoot + "/Data/FDS/test_core/test_core.smv"; reader->SetFileName(fileName); reader->UpdateInformation(); vtkDataAssembly* assembly = reader->GetAssembly(); if (!testValue(assembly->GetNumberOfChildren(0), 5, "number of root children")) { return false; } if (!testValue(assembly->GetNumberOfChildren(1), 2, "number of grids")) { return false; } if (!testValue(assembly->GetNumberOfChildren(2), 4, "number of devices")) { return false; } if (!testValue(assembly->GetNumberOfChildren(3), 1, "number of hrr")) { return false; } if (!testValue(assembly->GetNumberOfChildren(4), 11, "number of slices")) { return false; } if (!testValue(assembly->GetNumberOfChildren(5), 12, "number of boundaries")) { return false; } // Test extraction reader->AddSelector("/test_core/Grids/Mesh01"); reader->AddSelector("/test_core/Devices/HRR_3D"); reader->AddSelector("/test_core/HRR/test_core_hrr"); reader->AddSelector("/test_core/Slices/STRUCTURED_VelX_VELOCITY"); // Following slice contains cell-centered data reader->AddSelector("/test_core/Slices/STRUCTURED_TempZ_TEMPERATURE"); reader->AddSelector("/test_core/Boundaries/Mesh01_Blockage_3"); reader->Update(); vtkPartitionedDataSetCollection* output = vtkPartitionedDataSetCollection::SafeDownCast(reader->GetOutput()); vtkDataAssembly* outAssembly = output->GetDataAssembly(); if (!testValue(outAssembly->GetNumberOfChildren(0), 5, "number of root children")) { return false; } if (!testValue(outAssembly->GetNumberOfChildren(1), 1, "number of grids")) { return false; } if (!testValue(outAssembly->GetNumberOfChildren(2), 1, "number of devices")) { return false; } if (!testValue(outAssembly->GetNumberOfChildren(3), 1, "number of hrrs")) { return false; } // XXX: STRUCTURED_TempZ_TEMPERATURE covers 2 grids, resulting on having 3 slices. // See https://gitlab.kitware.com/paraview/paraview/-/issues/22683 if (!testValue(outAssembly->GetNumberOfChildren(4), 3, "number of slices")) { return false; } if (!testValue(outAssembly->GetNumberOfChildren(5), 1, "number of boundaries")) { return false; } // Test Mesh01 auto nodeIds = outAssembly->GetDataSetIndices(outAssembly->FindFirstNodeWithName("Mesh01")); auto* mesh01 = vtkRectilinearGrid::SafeDownCast(output->GetPartitionedDataSet(nodeIds[0])->GetPartition(0)); if (!mesh01) { std::cerr << "Mesh01 is nullptr" << std::endl; return false; } if (!testValue(mesh01->GetNumberOfPoints(), 7056, "number of points in Mesh01")) { return false; } if (!testValue(mesh01->GetNumberOfCells(), 6000, "number of points in Mesh01")) { return false; } // Test Device HRR_3D nodeIds = outAssembly->GetDataSetIndices(outAssembly->FindFirstNodeWithName("HRR_3D")); auto* hrr3D = vtkPolyData::SafeDownCast(output->GetPartitionedDataSet(nodeIds[0])->GetPartition(0)); if (!hrr3D) { std::cerr << "HRR_3D device is nullptr" << std::endl; return false; } if (!testValue(hrr3D->GetNumberOfPoints(), 1, "number of points in HRR_3D")) { return false; } if (!testValue(hrr3D->GetNumberOfCells(), 1, "number of points in HRR_3D")) { return false; } if (!testValue( hrr3D->GetPointData()->GetArray("Value")->GetComponent(0, 0), 0.0, "value of HRR_3D")) { return false; } // Test HRR nodeIds = outAssembly->GetDataSetIndices(outAssembly->FindFirstNodeWithName("test_core_hrr")); auto* hrr = vtkTable::SafeDownCast(output->GetPartitionAsDataObject(nodeIds[0], 0)); if (!hrr) { std::cerr << "HRR is nullptr" << std::endl; return false; } if (!testValue(hrr->GetRowData()->GetNumberOfArrays(), 13, "number of arrays in HRR table")) { return false; } if (!testValue( hrr->GetRowData()->GetArray(0)->GetComponent(0, 0), 0.0, "value of array in HRR table")) { return false; } // Test slice with point-centered data nodeIds = outAssembly->GetDataSetIndices(outAssembly->FindFirstNodeWithName("STRUCTURED_VelX_VELOCITY")); auto* sliceVelX = vtkRectilinearGrid::SafeDownCast(output->GetPartition(nodeIds[0], 0)); if (!sliceVelX) { std::cerr << "VelX slice is nullptr" << std::endl; return false; } if (!testValue(sliceVelX->GetNumberOfPoints(), 441, "number of points in sliceVelX")) { return false; } if (!testValue(sliceVelX->GetNumberOfCells(), 400, "number of cells in sliceVelX")) { return false; } if (!sliceVelX->GetPointData()->GetArray("Values")) { std::cerr << "VelX slice has no \"Values\" point data array." << std::endl; return false; } if (!testValue(sliceVelX->GetPointData()->GetArray("Values")->GetComponent(0, 0), 0.0, "value in VelX slice")) { return false; } // Test slice with cell-centered data nodeIds = outAssembly->GetDataSetIndices( outAssembly->FindFirstNodeWithName("STRUCTURED_TempZ_TEMPERATURE")); auto* sliceTempZ = vtkRectilinearGrid::SafeDownCast(output->GetPartition(nodeIds[0], 0)); if (!sliceTempZ) { std::cerr << "TempZ slice is nullptr" << std::endl; return false; } // XXX: Slice TempZ covering first grid only has only 336 points. // Total considering two parts (covering both grids) is 462. // See https://gitlab.kitware.com/paraview/paraview/-/issues/22683 if (!testValue(sliceTempZ->GetNumberOfPoints(), 336, "number of points in slice TempZ")) { return false; } // XXX: Slice TempZ covering first grid only has only 300 cells. // Total considering two parts (covering both grids) is 400. // See https://gitlab.kitware.com/paraview/paraview/-/issues/22683 if (!testValue(sliceTempZ->GetNumberOfCells(), 300, "number of cells in slice TempZ")) { return false; } if (!sliceTempZ->GetCellData()->GetArray("Values")) { std::cerr << "TempZ slice has no \"Values\" cell data array." << std::endl; return false; } if (!testValue(sliceTempZ->GetCellData()->GetArray("Values")->GetComponent(0, 0), 20.0, "value in TempZ slice")) { return false; } // Test boundary nodeIds = outAssembly->GetDataSetIndices(outAssembly->FindFirstNodeWithName("Mesh01_Blockage_3")); auto* boundary = vtkRectilinearGrid::SafeDownCast(output->GetPartition(nodeIds[0], 0)); if (!boundary) { std::cerr << "Mesh01_Blockage_3 boundary is nullptr" << std::endl; return false; } if (!testValue( boundary->GetNumberOfPoints(), 266, "number of points in Mesh01_Blockage_3 boundary")) { return false; } if (!testValue( boundary->GetNumberOfCells(), 234, "number of cells in Mesh01_Blockage_3 boundary")) { return false; } if (!boundary->GetPointData()->GetArray("gauge")) { std::cerr << "Mesh01_Blockage_3 boundary has no \"gauge\" point data array." << std::endl; return false; } constexpr double value_at_t0 = -0.00013127682905178517103195190429688; if (!testValueFuzzy(boundary->GetPointData()->GetArray("gauge")->GetComponent(0, 0), value_at_t0, "gauge in Mesh01_Blockage_3 boundary")) { return false; } if (!boundary->GetCellData()->GetArray("gauge")) { std::cerr << "Mesh01_Blockage_3 boundary has no \"gauge\" point data array." << std::endl; return false; } // Same value than before since no interpolation is done on the corner of the boundary if (!testValueFuzzy(boundary->GetCellData()->GetArray("gauge")->GetComponent(0, 0), value_at_t0, "gauge in Mesh01_Blockage_3 boundary")) { return false; } // Test number of timesteps auto* outInfo = reader->GetOutputInformation(0); if (!outInfo->Has(vtkStreamingDemandDrivenPipeline::TIME_STEPS())) { std::cerr << "Unable to retrieve timestep information " << std::endl; return false; } auto numberOfTimeSteps = outInfo->Length(vtkStreamingDemandDrivenPipeline::TIME_STEPS()); if (!testValue(numberOfTimeSteps, 31, "number of timesteps")) { return false; } // Now update timestep reader->UpdateTimeStep(8.1); output = vtkPartitionedDataSetCollection::SafeDownCast(reader->GetOutput()); outAssembly = output->GetDataAssembly(); nodeIds = outAssembly->GetDataSetIndices(outAssembly->FindFirstNodeWithName("Mesh01_Blockage_3")); boundary = vtkRectilinearGrid::SafeDownCast(output->GetPartition(nodeIds[0], 0)); constexpr double value_at_t8 = 0.935839116573333740234375; if (!testValueFuzzy(boundary->GetPointData()->GetArray("gauge")->GetComponent(0, 0), value_at_t8, "gauge in Mesh01_Blockage_3 boundary at time value 8.1")) { return false; } if (!testValueFuzzy(boundary->GetCellData()->GetArray("gauge")->GetComponent(0, 0), value_at_t8, "gauge (cell-centered) in Mesh01_Blockage_3 boundary at time value 8.1")) { return false; } return true; } int TestFDSReader(int argc, char* argv[]) { vtkNew testHelper; testHelper->AddArguments(argc, argv); if (!testHelper->IsFlagSpecified("-D")) { std::cerr << "Error: -D /path/to/data was not specified."; return EXIT_FAILURE; } std::string dataRoot = testHelper->GetDataRoot(); if (!TestExampleFile(dataRoot)) { return EXIT_FAILURE; } std::string tempDirectory = testHelper->GetTempDirectory(); if (!TestEmptyFile(tempDirectory)) { return EXIT_FAILURE; } return EXIT_SUCCESS; }