// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen // SPDX-FileCopyrightText: Copyright 2025 Sandia Corporation // SPDX-License-Identifier: LicenseRef-BSD-3-Clause-Sandia-USGov #include "vtkExtractStatisticalModelTables.h" #include "vtkDataAssembly.h" #include "vtkDataAssemblyVisitor.h" #include "vtkInformation.h" #include "vtkObjectFactory.h" #include "vtkPartitionedDataSet.h" #include "vtkPartitionedDataSetCollection.h" #include "vtkStatisticalModel.h" #include "vtkStringFormatter.h" #include "vtkTable.h" #include "vtksys/SystemTools.hxx" VTK_ABI_NAMESPACE_BEGIN namespace { std::string validTableName(const std::string& tableName) { return vtksys::SystemTools::MakeCidentifier(tableName); } void AddModelToAssembly(vtkPartitionedDataSetCollection* out, vtkStatisticalModel* model, vtkDataAssembly* assy, int rootAssyNode) { if (!model || model->IsEmpty()) { return; } // Iterate model tables, adding each to a new collection of \a out. // Add hierarchy to \a rootAssyNode of \a assy. unsigned int nextPartition = out->GetNumberOfPartitionedDataSets(); for (int tt = 0; tt < vtkStatisticalModel::GetNumberOfTableTypes(); ++tt) { auto tableType = static_cast(tt); int numTablesThisType = model->GetNumberOfTables(tt); if (!numTablesThisType) { continue; // no tables to add } auto tableTypeName = validTableName(vtkStatisticalModel::GetTableTypeName(tableType)); int typeNode = assy->AddNode(tableTypeName.c_str(), rootAssyNode); for (int ii = 0; ii < numTablesThisType; ++ii) { unsigned int dsidx = nextPartition++; auto rawTableName = model->GetTableName(tt, ii); auto tableName = validTableName(rawTableName); out->SetPartition(dsidx, 0, model->GetTable(tt, ii)); out->GetMetaData(dsidx)->Set(vtkCompositeDataSet::NAME(), rawTableName.c_str()); int node = assy->AddNode(tableName.c_str(), typeNode); assy->AddDataSetIndex(node, dsidx); } } } class ModelExtractor : public vtkDataAssemblyVisitor { public: static ModelExtractor* New(); vtkTypeMacro(ModelExtractor, vtkDataAssemblyVisitor); vtkPartitionedDataSetCollection* In; vtkPartitionedDataSetCollection* Out; vtkDataAssembly* AssemblyOut; void Initialize(vtkPartitionedDataSetCollection* pdc, vtkPartitionedDataSetCollection* out, vtkDataAssembly* resultAssy) { this->In = pdc; this->Out = out; this->AssemblyOut = resultAssy; } void Visit(int nodeId) override { auto indices = this->GetCurrentDataSetIndices(); int parentNode = nodeId; int midIdx = indices.size() > 1 ? 0 : -1; for (const auto& index : indices) { auto* pd = this->In->GetPartitionedDataSet(index); if (!pd || pd->GetNumberOfPartitions() == 0) { continue; } unsigned int np = pd->GetNumberOfPartitions(); if (np > 1 && midIdx < 0) { midIdx = 0; } // Loop over partitions. If any is a statistical model, add its tables. for (unsigned int ii = 0; ii < np; ++ii) { if (auto* model = vtkStatisticalModel::SafeDownCast(pd->GetPartitionAsDataObject(ii))) { if (midIdx >= 0) { std::string midNodeName = vtk::to_string(midIdx++); parentNode = this->AssemblyOut->AddNode(midNodeName.c_str(), nodeId); } AddModelToAssembly(this->Out, model, this->AssemblyOut, parentNode); } } } } }; } // anonymous namespace vtkStandardNewMacro(vtkExtractStatisticalModelTables); vtkStandardNewMacro(ModelExtractor); vtkExtractStatisticalModelTables::vtkExtractStatisticalModelTables() = default; vtkExtractStatisticalModelTables::~vtkExtractStatisticalModelTables() = default; void vtkExtractStatisticalModelTables::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); } int vtkExtractStatisticalModelTables::FillInputPortInformation(int port, vtkInformation* info) { (void)port; info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkPartitionedDataSetCollection"); info->Append(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkStatisticalModel"); return 1; } int vtkExtractStatisticalModelTables::RequestData(vtkInformation* vtkNotUsed(request), vtkInformationVector** inInfoVec, vtkInformationVector* outInfoVec) { auto* model = vtkStatisticalModel::GetData(inInfoVec[0]); auto* pdc = vtkPartitionedDataSetCollection::GetData(inInfoVec[0]); auto* out = vtkPartitionedDataSetCollection::GetData(outInfoVec); if (pdc && (pdc->GetNumberOfPartitionedDataSets() == 0 || !pdc->GetDataAssembly())) { // Empty input ⇒ empty output out->Initialize(); return 1; } vtkNew resultAssy; if (model) { AddModelToAssembly(out, model, resultAssy, /*root node of model*/ 0); } else if (pdc) { // Copy the input's structure so all node IDs match, but remove dataset references: auto* assemblyIn = pdc->GetDataAssembly(); resultAssy->DeepCopy(assemblyIn); resultAssy->RemoveAllDataSetIndices(0, /*traverse_subtree*/ true); // Create a visitor for the input collection. vtkNew extractor; extractor->Initialize(pdc, out, resultAssy); // Add new node IDs for statistical models by traversing the input: assemblyIn->Visit(extractor); } else { auto* data = vtkDataObject::GetData(inInfoVec[0]); vtkErrorMacro("Unhandled input type \"" << (data ? data->GetClassName() : "null") << "\"."); return 0; } out->SetDataAssembly(resultAssy); return 1; } VTK_ABI_NAMESPACE_END