/*========================================================================= Program: Visualization Toolkit Module: vtkNetCDFPOPReader.cxx Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ #include "vtkNetCDFPOPReader.h" #include "vtkCallbackCommand.h" #include "vtkDataArraySelection.h" #include "vtkFloatArray.h" #include "vtkInformation.h" #include "vtkInformationVector.h" #include "vtkObjectFactory.h" #include "vtkPointData.h" #include "vtkRectilinearGrid.h" #include "vtkSmartPointer.h" #include "vtkStreamingDemandDrivenPipeline.h" #include "vtk_netcdf.h" #include #include vtkStandardNewMacro(vtkNetCDFPOPReader); //============================================================================ #define CALL_NETCDF(call) \ { \ int errorcode = call; \ if (errorcode != NC_NOERR) \ { \ vtkErrorMacro(<< "netCDF Error: " << nc_strerror(errorcode)); \ return 0; \ } \ } //============================================================================ class vtkNetCDFPOPReaderInternal { public: vtkSmartPointer VariableArraySelection; // a mapping from the list of all variables to the list of available // point-based variables std::vector VariableMap; vtkNetCDFPOPReaderInternal() { this->VariableArraySelection = vtkSmartPointer::New(); } }; //------------------------------------------------------------------------------ // set default values vtkNetCDFPOPReader::vtkNetCDFPOPReader() { this->SetNumberOfInputPorts(0); this->SetNumberOfOutputPorts(1); this->FileName = nullptr; this->NCDFFD = 0; this->OpenedFileName = nullptr; this->Stride[0] = this->Stride[1] = this->Stride[2] = 1; this->SelectionObserver = vtkCallbackCommand::New(); this->SelectionObserver->SetCallback(&vtkNetCDFPOPReader::SelectionModifiedCallback); this->SelectionObserver->SetClientData(this); this->Internals = new vtkNetCDFPOPReaderInternal; this->Internals->VariableArraySelection->AddObserver( vtkCommand::ModifiedEvent, this->SelectionObserver); } //------------------------------------------------------------------------------ // delete filename and netcdf file descriptor vtkNetCDFPOPReader::~vtkNetCDFPOPReader() { this->SetFileName(nullptr); if (this->OpenedFileName) { nc_close(this->NCDFFD); this->SetOpenedFileName(nullptr); } if (this->SelectionObserver) { this->SelectionObserver->Delete(); this->SelectionObserver = nullptr; } delete this->Internals; this->Internals = nullptr; } //------------------------------------------------------------------------------ void vtkNetCDFPOPReader::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); os << indent << "FileName: " << (this->FileName ? this->FileName : "(nullptr)") << endl; os << indent << "OpenedFileName: " << (this->OpenedFileName ? this->OpenedFileName : "(nullptr)") << endl; os << indent << "Stride: {" << this->Stride[0] << ", " << this->Stride[1] << ", " << this->Stride[2] << ", " << "}" << endl; os << indent << "NCDFFD: " << this->NCDFFD << endl; this->Internals->VariableArraySelection->PrintSelf(os, indent.GetNextIndent()); } //------------------------------------------------------------------------------ // RequestInformation supplies global meta information // This should return the reality of what the reader is going to supply. // This retrieve the extents for the rectilinear grid // NC_MAX_VAR_DIMS comes from the nc library int vtkNetCDFPOPReader::RequestInformation(vtkInformation* vtkNotUsed(request), vtkInformationVector** vtkNotUsed(inputVector), vtkInformationVector* outputVector) { vtkInformation* outInfo = outputVector->GetInformationObject(0); if (this->FileName == nullptr) { vtkErrorMacro("FileName not set."); return 0; } if (this->OpenedFileName == nullptr || strcmp(this->OpenedFileName, this->FileName) != 0) { if (this->OpenedFileName) { nc_close(this->NCDFFD); this->SetOpenedFileName(nullptr); } int retval = nc_open(this->FileName, NC_NOWRITE, &this->NCDFFD); // read file if (retval != NC_NOERR) // checks if read file error { // we don't need to close the file if there was an error opening the file vtkErrorMacro(<< "Can't read file " << nc_strerror(retval)); return 0; } this->SetOpenedFileName(this->FileName); } // get number of variables from file int numberOfVariables; nc_inq_nvars(this->NCDFFD, &numberOfVariables); int dimidsp[NC_MAX_VAR_DIMS]; int extent[6]; int dataDimension; size_t dimensions[4]; // dimension value this->Internals->VariableMap.resize(numberOfVariables); char variableName[NC_MAX_NAME + 1]; int actualVariableCounter = 0; // For every variable in the file for (int i = 0; i < numberOfVariables; i++) { this->Internals->VariableMap[i] = -1; // get number of dimensions CALL_NETCDF(nc_inq_varndims(this->NCDFFD, i, &dataDimension)); // Variable Dimension ID's containing x,y,z coords for the rectilinear // grid spacing CALL_NETCDF(nc_inq_vardimid(this->NCDFFD, i, dimidsp)); if (dataDimension == 3) { this->Internals->VariableMap[i] = actualVariableCounter++; // get variable name CALL_NETCDF(nc_inq_varname(this->NCDFFD, i, variableName)); this->Internals->VariableArraySelection->AddArray(variableName); for (int m = 0; m < dataDimension; m++) { CALL_NETCDF(nc_inq_dimlen(this->NCDFFD, dimidsp[m], dimensions + m)); // acquire variable dimensions } extent[0] = extent[2] = extent[4] = 0; // set extent extent[1] = static_cast((dimensions[2] - 1) / this->Stride[0]); extent[3] = static_cast((dimensions[1] - 1) / this->Stride[1]); extent[5] = static_cast((dimensions[0] - 1) / this->Stride[2]); } } // fill in the extent information outInfo->Set(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), extent, 6); return 1; } //------------------------------------------------------------------------------ // Setting extents of the rectilinear grid int vtkNetCDFPOPReader::RequestData(vtkInformation* request, vtkInformationVector** vtkNotUsed(inputVector), vtkInformationVector* outputVector) { this->UpdateProgress(0); // the default implementation is to do what the old pipeline did find what // output is requesting the data, and pass that into ExecuteData // which output port did the request come from int outputPort = request->Get(vtkDemandDrivenPipeline::FROM_OUTPUT_PORT()); // if output port is negative then that means this filter is calling the // update directly, in that case just assume port 0 if (outputPort == -1) { outputPort = 0; } // get the data object vtkInformation* outInfo = outputVector->GetInformationObject(outputPort); vtkDataObject* output = outInfo->Get(vtkDataObject::DATA_OBJECT()); int subext[6]; // vtkInformation * outInfo = output->GetInformationObject(0); outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(), subext); vtkRectilinearGrid* rgrid = vtkRectilinearGrid::SafeDownCast(output); rgrid->SetExtent(subext); // setup extents for netcdf library to read the netcdf data file size_t start[] = { static_cast(subext[4] * this->Stride[2]), static_cast(subext[2] * this->Stride[1]), static_cast(subext[0] * this->Stride[0]) }; size_t count[] = { static_cast(subext[5] - subext[4] + 1), static_cast(subext[3] - subext[2] + 1), static_cast(subext[1] - subext[0] + 1) }; ptrdiff_t rStride[3] = { (ptrdiff_t)this->Stride[2], (ptrdiff_t)this->Stride[1], (ptrdiff_t)this->Stride[0] }; /* vtkMultiThreaderIDType pid = vtkMultiThreader::GetCurrentThreadID(); //The getpid() and getppid() functions are always successful, and no //return value is reserved to indicate an error. So is this check //necessary? if ((pid = vtkMultiThreader::GetCurrentThreadID()) == 0) { cerr<< "unable to get pid" << endl; } else { cerr << "The process id is " << pid << endl; } cerr << "subext " << subext[0] << " " << subext[1] << " " << subext[2] << " " << subext[3] << " " << subext[4] << " " << subext[5] << endl; */ // initialize memory (raw data space, x y z axis space) and rectilinear grid bool firstPass = true; for (size_t i = 0; i < this->Internals->VariableMap.size(); i++) { if (this->Internals->VariableMap[i] != -1 && this->Internals->VariableArraySelection->GetArraySetting(this->Internals->VariableMap[i]) != 0) { // varidp is probably i in which case nc_inq_varid isn't needed int varidp; nc_inq_varid(this->NCDFFD, this->Internals->VariableArraySelection->GetArrayName(this->Internals->VariableMap[i]), &varidp); if (firstPass) { int dimidsp[3]; nc_inq_vardimid(this->NCDFFD, varidp, dimidsp); firstPass = false; float* x = new float[count[0]]; float* y = new float[count[1]]; float* z = new float[count[2]]; // gets data from x,y,z axis (spacing) #if 0 nc_get_vara_float(this->NCDFFD, dimidsp[0], start, count, x); nc_get_vara_float(this->NCDFFD, dimidsp[1], start+1, count+1, y); nc_get_vara_float(this->NCDFFD, dimidsp[2], start+2, count+2, z); #else nc_get_vars_float(this->NCDFFD, dimidsp[0], start, count, rStride, x); nc_get_vars_float(this->NCDFFD, dimidsp[1], start + 1, count + 1, rStride + 1, y); nc_get_vars_float(this->NCDFFD, dimidsp[2], start + 2, count + 2, rStride + 2, z); #endif vtkFloatArray* xCoords = vtkFloatArray::New(); xCoords->SetArray(z, static_cast(count[2]), 0, 1); vtkFloatArray* yCoords = vtkFloatArray::New(); yCoords->SetArray(y, static_cast(count[1]), 0, 1); for (unsigned int q = 0; q < count[0]; q++) { x[q] = -x[q]; } vtkFloatArray* zCoords = vtkFloatArray::New(); zCoords->SetArray(x, static_cast(count[0]), 0, 1); rgrid->SetXCoordinates(xCoords); rgrid->SetYCoordinates(yCoords); rgrid->SetZCoordinates(zCoords); xCoords->Delete(); yCoords->Delete(); zCoords->Delete(); } // create vtkFloatArray and get the scalars into it vtkFloatArray* scalars = vtkFloatArray::New(); vtkIdType numberOfTuples = static_cast((count[0]) * (count[1]) * (count[2])); float* data = new float[numberOfTuples]; #if 0 nc_get_vara_float(this->NCDFFD, varidp, start, count, data); #else nc_get_vars_float(this->NCDFFD, varidp, start, count, rStride, data); #endif scalars->SetArray(data, numberOfTuples, 0, 1); // set list of variables to display data on rectilinear grid scalars->SetName( this->Internals->VariableArraySelection->GetArrayName(this->Internals->VariableMap[i])); rgrid->GetPointData()->AddArray(scalars); scalars->Delete(); } this->UpdateProgress((i + 1.0) / this->Internals->VariableMap.size()); } return 1; } //------------------------------------------------------------------------------ // following 5 functions are used for paraview user interface void vtkNetCDFPOPReader::SelectionModifiedCallback( vtkObject*, unsigned long, void* clientdata, void*) { static_cast(clientdata)->Modified(); } //------------------------------------------------------------------------------ int vtkNetCDFPOPReader::GetNumberOfVariableArrays() { return this->Internals->VariableArraySelection->GetNumberOfArrays(); } //------------------------------------------------------------------------------ const char* vtkNetCDFPOPReader::GetVariableArrayName(int index) { if (index < 0 || index >= this->GetNumberOfVariableArrays()) { return nullptr; } return this->Internals->VariableArraySelection->GetArrayName(index); } //------------------------------------------------------------------------------ int vtkNetCDFPOPReader::GetVariableArrayStatus(const char* name) { return this->Internals->VariableArraySelection->ArrayIsEnabled(name); } //------------------------------------------------------------------------------ void vtkNetCDFPOPReader::SetVariableArrayStatus(const char* name, int status) { vtkDebugMacro("Set cell array \"" << name << "\" status to: " << status); if (this->Internals->VariableArraySelection->ArrayExists(name) == 0) { vtkErrorMacro(<< name << " is not available in the file."); return; } int enabled = this->Internals->VariableArraySelection->ArrayIsEnabled(name); if (status != 0 && enabled == 0) { this->Internals->VariableArraySelection->EnableArray(name); this->Modified(); } else if (status == 0 && enabled != 0) { this->Internals->VariableArraySelection->DisableArray(name); this->Modified(); } }