/*=========================================================================

   Program: ParaView
   Module:  vtkCDIReader.cxx

   Copyright (c) 2005,2006 Sandia Corporation, Kitware Inc.
   All rights reserved.

   ParaView is a free software; you can redistribute it and/or modify it
   under the terms of the ParaView license version 1.2.

   See License_v1.2.txt for the full ParaView license.
   A copy of this license can be obtained by contacting
   Kitware Inc.
   28 Corporate Drive
   Clifton Park, NY 12065
   USA

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

========================================================================*/

/*-------------------------------------------------------------------------
   Copyright (c) 2018 Niklas Roeber, DKRZ Hamburg
  -------------------------------------------------------------------------*/

#include "vtkCDIReader.h"

#include "vtkCallbackCommand.h"
#include "vtkCellArray.h"
#include "vtkCellData.h"
#include "vtkCellType.h"
#include "vtkDataObject.h"
#include "vtkDummyController.h"
#include "vtkFieldData.h"
#include "vtkFileSeriesReader.h"
#include "vtkFloatArray.h"
#include "vtkIdTypeArray.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkPointData.h"
#include "vtkStreamingDemandDrivenPipeline.h"
#include "vtkStringArray.h"
#include "vtkUnstructuredGrid.h"

#include "vtksys/FStream.hxx"
#include "vtksys/SystemTools.hxx"

#include "cdi_tools.h"

#include <set>
#include <sstream>

namespace
{
class CDIObject
{
  enum stype
  {
    VOID,
    GRIB,
    NC
  };
  std::string URI;
  int StreamID;
  int VListID;
  stype Type;

public:
  CDIObject(std::string URI) { this->openURI(URI); }
  CDIObject()
  {
    this->StreamID = -1;
    this->setVoid();
  }
  ~CDIObject() { this->setVoid(); }
  int openURI(std::string URI)
  {
    this->setVoid();
    this->URI = URI;

    // check if we got either *.Grib or *.nc data
    std::string check = this->URI.substr((URI.size() - 4), this->URI.size());
    if (check == "grib" || check == ".grb")
    {
      this->Type = GRIB;
    }
    else
    {
      this->Type = NC;
    }

    this->StreamID = streamOpenRead(this->URI.c_str());
    if (this->StreamID < 0)
    {
      this->setVoid();
      return 0;
    }

    this->VListID = streamInqVlist(this->StreamID);
    return 1;
  }

  std::string getURI() const { return this->URI; }
  int getStreamID() const { return this->StreamID; }
  int getVListID() const { return this->VListID; }
  stype getType() const { return this->Type; }
  void setVoid()
  {
    if (this->StreamID > -1)
    {
      streamClose(this->StreamID);
    }

    this->StreamID = -1;
    this->VListID = -1;
    this->Type = VOID;
  }

  bool isVoid() const { return this->Type == VOID; }
};

struct Point
{
  double Lon;
  double Lat;
};

struct PointWithIndex
{
  Point Pt;
  int Idx;
};

constexpr static int MAX_VARS = 100;

struct Dimset
{
  size_t DimsetID;
  int GridID;
  int ZAxisID;
  size_t GridSize;
  int NLevel;
  std::string label;
};

struct Grid
{
  int GridID;
  size_t Size;
  int PointsPerCell;
};
}

//----------------------------------------------------------------------------
// Internal class to avoid name pollution
//----------------------------------------------------------------------------
class vtkCDIReader::Internal
{
public:
  Internal()
  {
    for (int i = 0; i < MAX_VARS; i++)
    {
      this->CellVarIDs[i] = -1;
      this->DomainVars[i] = std::string("");
    }
  }
  ~Internal() = default;

  int CellVarIDs[MAX_VARS];
  cdi_tools::CDIVar CellVars[MAX_VARS];
  cdi_tools::CDIVar PointVars[MAX_VARS];
  std::string DomainVars[MAX_VARS];

  // The Point data we expect to receive from each process.
  vtkSmartPointer<vtkIdTypeArray> PointsExpectedFromProcessesLengths;
  vtkSmartPointer<vtkIdTypeArray> PointsExpectedFromProcessesOffsets;

  // The Point data we have to send to each process.  Stored as global ids.
  vtkSmartPointer<vtkIdTypeArray> PointsToSendToProcesses;
  vtkSmartPointer<vtkIdTypeArray> PointsToSendToProcessesLengths;
  vtkSmartPointer<vtkIdTypeArray> PointsToSendToProcessesOffsets;

  std::map<std::string, Dimset> DimensionSets;
  std::vector<Grid> Grids;
  CDIObject DataFile, GridFile, VGridFile;
};

namespace
{
//----------------------------------------------------------------------------
// Macro to check new didn't return an error
//----------------------------------------------------------------------------
#define CHECK_NEW(ptr)                                                                             \
  if ((ptr) == nullptr)                                                                            \
  {                                                                                                \
    vtkErrorMacro("new failed!");                                                                  \
    return 0;                                                                                      \
  }

//----------------------------------------------------------------------------
//  Macros for template calls to load cell/Point variables
//----------------------------------------------------------------------------
#define vtkICONTemplateMacro(call)                                                                 \
  vtkTemplateMacroCase(VTK_DOUBLE, double, call);                                                  \
  vtkTemplateMacroCase(VTK_FLOAT, float, call);

#define vtkICONTemplateDispatch(type, call)                                                        \
  switch (type)                                                                                    \
  {                                                                                                \
    vtkICONTemplateMacro(call);                                                                    \
    default:                                                                                       \
      vtkErrorMacro("Unsupported data type: " << (type));                                          \
      abort();                                                                                     \
  }

//----------------------------------------------------------------------------
// Routines for sorting and efficient removal of duplicates
// (c) and thanks to Moritz Hanke (DKRZ)
//----------------------------------------------------------------------------
int ComparePointWithIndex(const void* a, const void* b)
{
  const struct PointWithIndex* a_ = (struct PointWithIndex*)a;
  const struct PointWithIndex* b_ = (struct PointWithIndex*)b;
  double threshold = 1e-22;

  int lon_diff = fabs(a_->Pt.Lon - b_->Pt.Lon) > threshold;
  if (lon_diff)
  {
    return (a_->Pt.Lon > b_->Pt.Lon) ? -1 : 1;
  }

  int lat_diff = fabs(a_->Pt.Lat - b_->Pt.Lat) > threshold;
  if (lat_diff)
  {
    return (a_->Pt.Lat > b_->Pt.Lat) ? -1 : 1;
  }
  return 0;
}
} // end of anonymous namepace

vtkStandardNewMacro(vtkCDIReader);

vtkCxxSetObjectMacro(vtkCDIReader, Controller, vtkMultiProcessController);

//----------------------------------------------------------------------------
// Constructor for vtkCDIReader
//----------------------------------------------------------------------------
vtkCDIReader::vtkCDIReader()
  : Internals(new Internal())
{
  vtkDebugMacro("Starting to create vtkCDIReader...");

  this->SetNumberOfInputPorts(0);
  this->SetNumberOfOutputPorts(1);

  // Setup selection callback to modify this object when array selection changes
  this->SelectionObserver->SetCallback(&vtkCDIReader::SelectionCallback);
  this->SelectionObserver->SetClientData(this);
  this->CellDataArraySelection->AddObserver(vtkCommand::ModifiedEvent, this->SelectionObserver);
  this->PointDataArraySelection->AddObserver(vtkCommand::ModifiedEvent, this->SelectionObserver);
  this->DomainDataArraySelection->AddObserver(vtkCommand::ModifiedEvent, this->SelectionObserver);

  this->SetController(vtkMultiProcessController::GetGlobalController());
  if (!this->Controller)
  {
    vtkNew<vtkDummyController> dummyController;
    this->SetController(dummyController);
  }

  vtkDebugMacro("MAX_VARS:" << MAX_VARS);
  vtkDebugMacro("Created vtkCDIReader");
}

//----------------------------------------------------------------------------
//  Destroys data stored for variables, points, and cells, but
//  doesn't destroy the list of variables or toplevel cell/pointVarDataArray.
//----------------------------------------------------------------------------
void vtkCDIReader::DestroyData()
{
  vtkDebugMacro("DestroyData...");
  vtkDebugMacro("Destructing double cell var data...");

  this->CellVarDataArray->Initialize();

  vtkDebugMacro("Destructing double Point var array...");
  this->PointVarDataArray->Initialize();

  this->DomainVarDataArray->Initialize();

  vtkDebugMacro("Out DestroyData...");
}

//----------------------------------------------------------------------------
// Destructor for vtkCDIReader
//----------------------------------------------------------------------------
vtkCDIReader::~vtkCDIReader()
{
  vtkDebugMacro("Destructing vtkCDIReader...");

  this->DestroyData();

  vtkDebugMacro("Destructing other stuff...");
  this->PointDataArraySelection->RemoveObserver(this->SelectionObserver);
  this->CellDataArraySelection->RemoveObserver(this->SelectionObserver);
  this->DomainDataArraySelection->RemoveObserver(this->SelectionObserver);

  this->SetController(nullptr);

  vtkDebugMacro("Destructed vtkCDIReader");
}

// Grab info from outInfo object (method extracted from RequestInformation)
//----------------------------------------------------------------------------
void vtkCDIReader::GetFileSeriesInformation(vtkInformation* outInfo)
{
  if (outInfo->Has(vtkFileSeriesReader::FILE_SERIES_NUMBER_OF_FILES()))
  {
    this->NumberOfFiles = outInfo->Get(vtkFileSeriesReader::FILE_SERIES_NUMBER_OF_FILES());
  }
  if (outInfo->Has(vtkFileSeriesReader::FILE_SERIES_CURRENT_FILE_NUMBER()))
  {
    this->FileSeriesNumber = outInfo->Get(vtkFileSeriesReader::FILE_SERIES_CURRENT_FILE_NUMBER());
  }
  if (outInfo->Has(vtkFileSeriesReader::FILE_SERIES_FIRST_FILENAME()))
  {
    this->FileSeriesFirstName = outInfo->Get(vtkFileSeriesReader::FILE_SERIES_FIRST_FILENAME());
  }
}

//----------------------------------------------------------------------------
// Verify that the file exists, get dimension sizes and variables
//----------------------------------------------------------------------------
int vtkCDIReader::RequestInformation(
  vtkInformation* reqInfo, vtkInformationVector** inVector, vtkInformationVector* outVector)
{
  vtkDebugMacro("In vtkCDIReader::RequestInformation");
  if (!this->Superclass::RequestInformation(reqInfo, inVector, outVector))
  {
    return 0;
  }

  if (this->FileName.empty())
  {
    vtkErrorMacro("No filename specified");
    return 0;
  }

  if (this->Controller->GetNumberOfProcesses() > 1)
  {
    this->Decomposition = true;
    this->NumberOfProcesses = this->Controller->GetNumberOfProcesses();
  }

  vtkDebugMacro("In vtkCDIReader::RequestInformation read filename ok: " << this->FileName.c_str());
  vtkInformation* outInfo = outVector->GetInformationObject(0);

  vtkDebugMacro("FileName: " << this->FileName.c_str());

  if (!this->GetDims())
  {
    return 0;
  }

  this->InfoRequested = true;

  vtkDebugMacro("In vtkCDIReader::RequestInformation setting VerticalLevelRange");
  this->VerticalLevelRange[0] = 0;
  if (this->VerticalLevelRange[1] != this->MaximumNVertLevels - 1)
  {
    this->VerticalLevelRange[1] = this->MaximumNVertLevels - 1;
    this->Modified();
  }

  if (!this->GetVars())
  {
    return 0;
  }

  this->ResetVarDataArrays();

  this->GetFileSeriesInformation(outInfo);

  vtkSmartPointer<vtkDoubleArray> timeValues = this->ReadTimeAxis();

  this->OutputTimeAxis(outInfo, timeValues);

  outInfo->Set(CAN_HANDLE_PIECE_REQUEST(), 1);
  vtkDebugMacro("Out vtkCDIReader::RequestInformation");

  return 1;
}

//----------------------------------------------------------------------------
vtkSmartPointer<vtkDoubleArray> vtkCDIReader::ReadTimeAxis()
{
  if ((this->FileSeriesNumber == 0) && (!this->TimeSet))
  {

    int taxisID = vlistInqTaxis(this->Internals->DataFile.getVListID());
    int calendar = taxisInqCalendar(taxisID);
    streamInqTimestep(this->Internals->DataFile.getStreamID(), 0);
    int vdate = taxisInqVdate(taxisID);
    int vtime = taxisInqVtime(taxisID);

    this->FirstDay = date_to_julday(calendar, vdate);
    this->TimeUnits = cdi_tools::ParseTimeUnits(vdate, vtime);
    this->Calendar = cdi_tools::ParseCalendar(calendar);
    this->TimeSet = true;
  }

  if (!this->TimeSeriesTimeStepsAllSet)
  {
    if (this->TimeSeriesTimeSteps.size() < this->FileSeriesNumber + 1)
    {
      this->TimeSeriesTimeSteps.resize(this->FileSeriesNumber + 1);
    }
    this->TimeSeriesTimeSteps[this->FileSeriesNumber] = this->NumberOfTimeSteps;
  }

  this->NumberOfAllTimeSteps = 0;
  for (int step = 0; step < this->NumberOfFiles; step++)
  {
    this->NumberOfAllTimeSteps += this->TimeSeriesTimeSteps[step];
  }

  auto timeValues = vtkSmartPointer<vtkDoubleArray>::New();
  timeValues->Allocate(this->NumberOfTimeSteps);
  timeValues->SetNumberOfComponents(1);
  int start = 0;
  int counter = 0;
  for (int step = 0; step < this->FileSeriesNumber; step++)
  {
    start += this->TimeSeriesTimeSteps[step];
  }
  int end = start + this->NumberOfTimeSteps;
  for (int step = start; step < end; step++)
  {
    int taxisID = vlistInqTaxis(this->Internals->DataFile.getVListID());
    int calendar = taxisInqCalendar(taxisID);
    streamInqTimestep(this->Internals->DataFile.getStreamID(), counter);
    int vdate = taxisInqVdate(taxisID);
    int vtime = taxisInqVtime(taxisID);
    double timevalue = date_to_julday(calendar, vdate);
    if (vtime > 0)
    {
      timevalue += (double)((time_to_sec(vtime)) / 86400.0);
    }
    timevalue -= this->FirstDay;
    if (timevalue >= 0.0)
    {
      timeValues->InsertNextTuple1(timevalue);
      if (!this->TimeSeriesTimeStepsAllSet)
      {
        if (this->TimeSteps.size() < step + 1)
        {
          this->TimeSteps.resize(step + 1);
        }
        this->TimeSteps[step] = timevalue;
      }
    }
    else
    {
      this->NumberOfTimeSteps -= 1;
    }
    counter++;
  }

  if (this->FileSeriesNumber == (this->NumberOfFiles - 1))
  {
    this->TimeSeriesTimeStepsAllSet = true;
  }

  return timeValues;
}

// Call DestroyData() before calling this!
//----------------------------------------------------------------------------
void vtkCDIReader::ResetVarDataArrays()
{
  this->PointVarDataArray->AllocateArrays(this->NumberOfPointVars);
  this->CellVarDataArray->AllocateArrays(this->NumberOfCellVars);
  this->DomainVarDataArray->AllocateArrays(this->NumberOfDomainVars);
}

// Add the time axis to the out info
//----------------------------------------------------------------------------
void vtkCDIReader::OutputTimeAxis(
  vtkInformation* outInfo, const vtkSmartPointer<vtkDoubleArray>& timeValues)
{
  if (this->NumberOfTimeSteps > 0)
  {
    outInfo->Set(vtkStreamingDemandDrivenPipeline::TIME_STEPS(), timeValues->GetPointer(0),
      timeValues->GetNumberOfTuples());
    double timeRange[2];
    timeRange[0] = timeValues->GetValue(0);
    timeRange[1] = timeValues->GetValue(timeValues->GetNumberOfTuples() - 1);
    outInfo->Set(vtkStreamingDemandDrivenPipeline::TIME_RANGE(), timeRange, 2);
  }
}

//----------------------------------------------------------------------------
int vtkCDIReader::GetTimeIndex(double dataTimeStep)
{
  int start = 0;
  for (int step = 0; step < this->FileSeriesNumber; step++)
    start += this->TimeSeriesTimeSteps[step];
  int end = start + this->TimeSeriesTimeSteps[this->FileSeriesNumber];
  for (int step = start; step < end; step++)
  {
    if (this->TimeSteps[step] == dataTimeStep)
    {
      return (step - start);
    }
  }
  return 0;
}

//----------------------------------------------------------------------------
int vtkCDIReader::RequestUpdateExtent(
  vtkInformation*, vtkInformationVector**, vtkInformationVector* outputVector)
{
  vtkInformation* outInfo = outputVector->GetInformationObject(0);
  int piece = outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_PIECE_NUMBER());
  int numPieces = outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_PIECES());
  // make sure piece is valid
  return (piece < 0 || piece >= numPieces) ? 0 : 1;
}

//----------------------------------------------------------------------------
long vtkCDIReader::GetPartitioning(int piece, int numPieces, int numCellsPerLevel,
  int numPointsPerCell, int& beginPoint, int& endPoint, int& beginCell, int& endCell)
{
  if (numPieces == 1)
  {
    beginPoint = 0;
    endPoint = (numCellsPerLevel * numPointsPerCell) - 1;
    beginCell = 0;
    endCell = numCellsPerLevel - 1;

    return numCellsPerLevel;
  }
  else
  {
    long localCells = 0;
    int cells_per_piece = numCellsPerLevel / numPieces;
    if (piece == 0)
    {
      beginCell = 0;
      endCell = (piece + 1) * cells_per_piece - 1;
      beginPoint = 0;
      endPoint = ((endCell + 1) * numPointsPerCell) - 1;
      localCells = 1 + endCell;
    }
    else if (piece < (numPieces - 1))
    {
      beginCell = piece * cells_per_piece;
      endCell = (piece + 1) * cells_per_piece;
      beginPoint = beginCell * numPointsPerCell;
      endPoint = (endCell * numPointsPerCell) - 1;
      localCells = endCell - beginCell;
    }
    else if (piece == (numPieces - 1))
    {
      beginCell = piece * cells_per_piece;
      endCell = numCellsPerLevel - 1;
      beginPoint = beginCell * numPointsPerCell;
      endPoint = ((endCell + 1) * numPointsPerCell) - 1;
      localCells = 1 + endCell - beginCell;
    }
    return localCells;
  }
}

//----------------------------------------------------------------------------
// Default method: Data is read into a vtkUnstructuredGrid
//----------------------------------------------------------------------------
int vtkCDIReader::RequestData(vtkInformation* vtkNotUsed(reqInfo),
  vtkInformationVector** vtkNotUsed(inVector), vtkInformationVector* outVector)
{
  vtkDebugMacro("In vtkCDIReader::RequestData");
  vtkInformation* outInfo = outVector->GetInformationObject(0);

  if (outInfo->Has(vtkFileSeriesReader::FILE_SERIES_CURRENT_FILE_NUMBER()))
  {
    this->FileSeriesNumber = outInfo->Get(vtkFileSeriesReader::FILE_SERIES_CURRENT_FILE_NUMBER());
  }

  this->Piece = outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_PIECE_NUMBER());
  this->NumPieces = outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_PIECES());
  this->NumberLocalCells = this->GetPartitioning(this->Piece, this->NumPieces, this->NumberOfCells,
    this->PointsPerCell, this->BeginPoint, this->EndPoint, this->BeginCell, this->EndCell);

  if (this->DataRequested)
  {
    this->DestroyData();
  }
  if ((!this->Initialized) || (!this->SkipGrid))
  {
    if (!this->ReadAndOutputGrid(true))
    {
      return 0;
    }
  }
  this->Initialized = true;

  double requestedTimeStep = 0.;
#ifndef NDEBUG
  int numRequestedTimeSteps = 0;
#endif

  vtkInformationDoubleKey* timeKey =
    static_cast<vtkInformationDoubleKey*>(vtkStreamingDemandDrivenPipeline::UPDATE_TIME_STEP());
  if (outInfo->Has(timeKey))
  {
#ifndef NDEBUG
    numRequestedTimeSteps = 1;
#endif
    requestedTimeStep = outInfo->Get(timeKey);
  }

  vtkDebugMacro("Num Time steps requested: " << numRequestedTimeSteps);
  this->DTime = requestedTimeStep;
  vtkDebugMacro("this->DTime: " << this->DTime);
  double dTimeTemp = this->DTime;
  this->Output->GetInformation()->Set(vtkDataObject::DATA_TIME_STEP(), dTimeTemp);
  vtkDebugMacro("dTimeTemp: " << dTimeTemp);
  this->DTime = dTimeTemp;

  for (int var = 0; var < this->NumberOfCellVars; var++)
  {
    if (this->GetCellArrayStatus(this->Internals->CellVars[var].Name))
    {
      vtkDebugMacro("Loading Cell Variable: " << this->Internals->CellVars[var].Name);
      this->LoadCellVarData(var, this->DTime);
    }
    else
      vtkDebugMacro(
        "Ignoring Cell Variable: " << this->Internals->CellVars[var].Name << " as requested ");
  }
  this->Output->GetCellData()->ShallowCopy(this->CellVarDataArray);

  for (int var = 0; var < this->NumberOfPointVars; var++)
  {
    if (this->GetPointArrayStatus(this->Internals->PointVars[var].Name))
    {
      vtkDebugMacro("Loading Point Variable: " << var);
      this->LoadPointVarData(var, this->DTime);
    }
  }
  this->Output->GetPointData()->ShallowCopy(this->PointVarDataArray);

  for (int var = 0; var < this->NumberOfDomainVars; var++)
  {
    if (this->GetDomainArrayStatus(this->Internals->DomainVars[var].c_str()))
    {
      vtkDebugMacro("Loading Domain Variable: " << this->Internals->DomainVars[var].c_str());
      this->LoadDomainVarData(var);
    }
  }
  this->Output->GetFieldData()->ShallowCopy(this->DomainVarDataArray);

  if (!this->TimeUnits.empty())
  {
    vtkNew<vtkStringArray> arr;
    arr->SetName("time_units");
    arr->InsertNextValue(this->TimeUnits);
    this->Output->GetFieldData()->AddArray(arr);
  }
  if (!this->Calendar.empty())
  {
    vtkNew<vtkStringArray> arr;
    arr->SetName("time_calendar");
    arr->InsertNextValue(this->Calendar);
    this->Output->GetFieldData()->AddArray(arr);
  }

  if (this->BuildDomainArrays)
  {
    this->BuildDomainArrays = this->BuildDomainCellVars();
  }

  this->DataRequested = true;
  vtkDebugMacro("Returning from RequestData");

  vtkUnstructuredGrid::GetData(outVector)->ShallowCopy(this->Output);

  return 1;
}

//----------------------------------------------------------------------------
// Regenerate and reread the data variables available
//----------------------------------------------------------------------------
int vtkCDIReader::RegenerateVariables()
{
  vtkDebugMacro("In RegenerateVariables");
  this->NumberOfPointVars = 0;
  this->NumberOfCellVars = 0;
  this->NumberOfDomainVars = 0;

  if (this->FileName.empty() || !this->GetDims())
  {
    return 0;
  }

  this->VerticalLevelRange[0] = 0;
  this->VerticalLevelRange[1] = this->MaximumNVertLevels - 1;

  if (!this->BuildVarArrays())
  {
    return 0;
  }

  // Allocate the ParaView data arrays which will hold the variables
  this->ResetVarDataArrays();

  vtkDebugMacro("Returning from RegenerateVariables");
  return 1;
}

//----------------------------------------------------------------------------
// If the user changes the projection, or singlelayer to
// multilayer, we need to regenerate the geometry.
//----------------------------------------------------------------------------
int vtkCDIReader::RegenerateGeometry()
{
  vtkDebugMacro("RegenerateGeometry ...");

  if (this->GridReconstructed)
  {
    vtkDebugMacro("GridReconstructed!");
    if (!this->ReadAndOutputGrid(true))
    {
      return 0;
    }
  }
  else
  {
    vtkDebugMacro("GridReconstructed=false");
  }

  double dTimeTemp = this->DTime;
  this->Output->GetInformation()->Set(vtkDataObject::DATA_TIME_STEP(), dTimeTemp);
  this->DTime = dTimeTemp;

  for (int var = 0; var < this->NumberOfCellVars; var++)
  {
    if (this->GetCellArrayStatus(this->Internals->CellVars[var].Name))
    {
      vtkDebugMacro("Loading Cell Variable: " << this->Internals->CellVars[var].Name);
      this->LoadCellVarData(var, this->DTime);
    }
  }
  this->Output->GetCellData()->ShallowCopy(this->CellVarDataArray);
  for (int var = 0; var < this->NumberOfPointVars; var++)
  {
    if (this->GetPointArrayStatus(this->Internals->PointVars[var].Name))
    {
      vtkDebugMacro("Loading Point Variable: " << var);
      this->LoadPointVarData(var, this->DTime);
    }
  }
  this->Output->GetPointData()->ShallowCopy(this->PointVarDataArray);

  for (int var = 0; var < this->NumberOfDomainVars; var++)
  {
    if (this->GetDomainArrayStatus(this->Internals->DomainVars[var].c_str()))
    {
      vtkDebugMacro("Loading Domain Variable: " << this->Internals->DomainVars[var].c_str());
      this->LoadDomainVarData(var);
    }
  }
  this->Output->GetFieldData()->ShallowCopy(this->DomainVarDataArray);

  this->PointDataArraySelection->Modified();
  this->CellDataArraySelection->Modified();
  this->Modified();

  return 1;
}

//----------------------------------------------------------------------------
// Get dimensions of key NetCDF variables
//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
void vtkCDIReader::GuessGridFile()
{
  std::string fallback = vtksys::SystemTools::GetParentDirectory(this->FileName);
  if (fallback.empty())
  {
    fallback = ".";
  }
  fallback += "/grid.nc";

  std::string guess;
  if (!this->Grib)
  {
    guess = cdi_tools::GuessGridFileFromUri(this->FileName);
  }

  if (!guess.empty())
  {
    if (vtksys::SystemTools::TestFileAccess(guess, vtksys::TEST_FILE_READ))
    {
      this->Internals->GridFile.openURI(guess);
      if (this->Internals->GridFile.isVoid())
      {
        vtkWarningMacro("Cannot handle grid file "
          << guess << " indicated by grid_file_uri attribute in " << this->FileName
          << " Trying fallback guess " << fallback);
      }
      else
      {
        return;
      }
    }
    else
    {
      vtkWarningMacro("Cannot open grid file "
        << guess << " indicated by grid_file_uri attribute in " << this->FileName
        << " Trying fallback guess " << fallback);
    }
  }
  this->Internals->GridFile.openURI(fallback);
}

//----------------------------------------------------------------------------
// Get dimensions of key NetCDF variables
//----------------------------------------------------------------------------
int vtkCDIReader::GetDims()
{
  if (this->FileName.empty())
  {
    vtkErrorMacro("No file name provided. Cannot get dimensions");
    return 0;
  }

  this->Internals->DataFile.openURI(this->FileName);
  if (this->Internals->DataFile.isVoid())
  {
    vtkErrorMacro("GetDims: Could not open " << this->Internals->DataFile.getURI());
    return 0;
  }

  if (this->Internals->GridFile.isVoid())
  {
    this->Internals->GridFile.openURI(this->FileName);
  }

  if (this->Internals->GridFile.isVoid())
  {
    vtkErrorMacro("GetDims: Could not open horizontal grid file.\nTried "
      << this->Internals->GridFile.getURI());
    return 0;
  }

  if (!this->ReadHorizontalGridData())
  {
    this->GuessGridFile();
    if (!this->ReadHorizontalGridData())
    {
      vtkErrorMacro(
        "Could not get horizontal Grid. \nTried " << this->Internals->GridFile.getURI());
      return 0;
    }
  }

  this->Internals->VGridFile.openURI(this->FileName);
  int found = this->ReadVerticalGridData();
  if (!found)
  {
    this->Internals->VGridFile.openURI(this->Internals->GridFile.getURI());
    found = this->ReadVerticalGridData();
  }

  if (!found)
  {
    vtkErrorMacro("Could not get Vertical grid");
    return 0;
  }

  this->FillGridDimensions();

  if (this->DimensionSelection.empty())
  {
    // select first by default
    this->DimensionSelection = this->Internals->DimensionSets.begin()->first;
  }
  for (int i = 0; i < this->Internals->Grids.size(); i++)
  {
    if (this->Internals->DimensionSets.at(this->DimensionSelection).GridSize ==
      this->Internals->Grids.at(i).Size)
    {
      this->Internals->DimensionSets.at(this->DimensionSelection).GridID =
        this->Internals->Grids.at(i).GridID;
      this->GridID = i;
    }
  }
  this->ZAxisID = this->Internals->DimensionSets.at(this->DimensionSelection).ZAxisID;

  try
  {
    if (this->GridID != -1 && this->Internals->Grids.at(this->GridID).GridID != -1)
    {
      this->NumberOfCells = static_cast<int>(this->Internals->Grids.at(GridID).Size);

      if (this->NumberOfPoints and this->NumberOfPoints != this->NumberOfCells)
      {
        vtkDebugMacro("GetDims: Changing number of points from  " << this->NumberOfPoints << " to "
                                                                  << this->NumberOfCells);
      }
      this->NumberOfPoints = this->NumberOfCells;
      this->PointsPerCell = this->Internals->Grids.at(this->GridID).PointsPerCell;
      vtkDebugMacro("GetDims: Found PointsPerCell to be  " << this->PointsPerCell << " for grid  "
                                                           << this->GridID);
    }
  }
  catch (const std::out_of_range& oor)
  {
    vtkErrorMacro("Out of Range error in GetDims trying to set NumberOfPoints " << oor.what());
    vtkErrorMacro("Grids.size " << this->Internals->Grids.size() << "\t GridID " << this->GridID);
    return 0;
  }

  int ntsteps = 0;
  if (this->Grib)
  {
    while (streamInqTimestep(this->Internals->DataFile.getStreamID(), ntsteps))
    {
      ntsteps++;
    }
  }
  else
  {
    ntsteps = vlistNtsteps(this->Internals->DataFile.getVListID());
  }
  this->NumberOfTimeSteps = ntsteps;

  this->MaximumNVertLevels = 1;
  if (this->ZAxisID != -1)
  {
    this->MaximumNVertLevels = zaxisInqSize(this->ZAxisID);
  }

  return 1;
}

//---------------------------------------------------------------------------------------------------
// Read Horizontal Grid Data
// Checks if there is at least one grid with >= 3 vertices, and if yes, sets GridID to this grid's
// ID
//---------------------------------------------------------------------------------------------------
int vtkCDIReader::ReadHorizontalGridData()
{
  this->Internals->Grids.resize(0);
  int vListID = this->Internals->GridFile.getVListID();
  if (vListID == CDI_UNDEFID)
  {
    vtkErrorMacro("No VList found in Grid file.");
    return 0;
  }
  int ngrids = vlistNgrids(vListID);
  for (int i = 0; i < ngrids; ++i)
  {
    int gridID_l = vlistGrid(vListID, i);
    int nv = gridInqNvertex(gridID_l);

    if (nv >= 3) //  ((nv == 3 || nv == 4)) // && gridInqType(gridID_l) == GRID_UNSTRUCTURED)
    {
      Grid grid{
        .GridID = gridID_l, .Size = static_cast<size_t>(gridInqSize(gridID_l)), .PointsPerCell = nv
      };
      this->Internals->Grids.push_back(grid);
    }
  }

  if (this->Internals->Grids.empty())
  {
    return 0;
  }
  return 1;
}

//----------------------------------------------------------------------------
// Read Vertical Grid Data
//----------------------------------------------------------------------------
int vtkCDIReader::ReadVerticalGridData()
{
  this->ZAxisID = -1;
  int nzaxis = vlistNzaxis(this->Internals->VGridFile.getVListID());
  int found = 0;
  for (int i = 0; i < nzaxis; ++i)
  {
    int zaxisID_l = vlistZaxis(this->Internals->VGridFile.getVListID(), i);
    if (zaxisInqSize(zaxisID_l) == 1 || zaxisInqType(zaxisID_l) == ZAXIS_SURFACE)
    {
      this->SurfIDs.insert(zaxisID_l);

      found = 1;
    }
  }

  for (int i = 0; i < nzaxis; ++i)
  {
    int zaxisID_l = vlistZaxis(this->Internals->VGridFile.getVListID(), i);
    if (zaxisInqSize(zaxisID_l) > 1)
    {
      found = 1;
      break;
    }
  }

  return found;
}

//----------------------------------------------------------------------------
// Get the NetCDF variables on cell and vertex and check for domain data
//----------------------------------------------------------------------------
int vtkCDIReader::GetVars()
{
  int cellVarIndex = -1;
  int pointVarIndex = -1;
  int domainVarIndex = -1;
  int numVars = vlistNvars(this->Internals->DataFile.getVListID());

  vtkDebugMacro(
    "Found " << numVars << " as Variables for VListID " << this->Internals->DataFile.getVListID());

  for (int i = 0; i < numVars; i++)
  {
    int varID = i;
    cdi_tools::CDIVar aVar;

    aVar.StreamID = this->Internals->DataFile.getStreamID();
    aVar.VarID = varID;
    aVar.GridID = vlistInqVarGrid(this->Internals->DataFile.getVListID(), varID);
    aVar.ZAxisID = vlistInqVarZaxis(this->Internals->DataFile.getVListID(), varID);
    aVar.GridSize = static_cast<int>(gridInqSize(aVar.GridID));
    aVar.NLevel = zaxisInqSize(aVar.ZAxisID);
    aVar.Type = 0;
    aVar.ConstTime = 0;
    vlistInqVarName(this->Internals->DataFile.getVListID(), varID, aVar.Name);
    vtkDebugMacro("Processing variable " << i << '\t' << aVar.Name);

    // to do multiple grids:
    // - Check how many grids are available
    // - Check if all grids can be reconstructed, or if bnds are all zero
    // - Reform gui to load either Cell, Point or Edge data

    if (vlistInqVarTsteptype(this->Internals->DataFile.getVListID(), varID) == TIME_CONSTANT)
    {
      aVar.ConstTime = 1;
    }
    if (aVar.ZAxisID != this->ZAxisID && this->SurfIDs.count(aVar.ZAxisID) == 0)
    // We are handling a different 3D Axis.
    {
      vtkDebugMacro("Skipping " << aVar.Name << " as it has the wrong ZAxis " << aVar.ZAxisID);
      continue;
    }

    aVar.Type = 2;
    if (aVar.NLevel > 1)
    {
      aVar.Type = 3;
    }
    int varType = aVar.Type;

    // check for dim 2 being cell or Point
    bool isCellData = false;
    bool isPointData = false;
    if (aVar.GridSize == this->NumberOfCells)
    {
      isCellData = true;
    }
    else if ((aVar.GridSize < this->NumberOfCells) && (this->PointsPerCell == 3))
    {
      vtkDebugMacro("Skipping " << aVar.Name << " as it has the wrong GridSize " << aVar.GridSize
                                << " != " << this->NumberOfCells);
      if (this->NumberOfPoints and this->NumberOfPoints != aVar.GridSize)
      {
        vtkWarningMacro("Not adding "
          << aVar.Name << " as point var, as it's size " << aVar.GridSize
          << " does not correspond to our understanding of the correct size for 'the' point grid: "
          << this->NumberOfPoints);
        continue;
      }
      isPointData = true;
      this->NumberOfPoints = aVar.GridSize;
    }
    else
    {
      vtkDebugMacro("Skipping " << aVar.Name << " as it has the wrong GridSize " << aVar.GridSize
                                << " != " << this->NumberOfCells);
      continue;
    }

    // 3D variables ...
    if (varType == 3)
    {
      if (isCellData)
      {
        cellVarIndex++;
        if (cellVarIndex > MAX_VARS - 1)
        {
          vtkErrorMacro("Exceeded number of cell vars.");
          return 0;
        }
        this->Internals->CellVars[cellVarIndex] = aVar;
        vtkDebugMacro("Adding var " << aVar.Name << " to CellVars");
      }
      else if (isPointData)
      {
        pointVarIndex++;
        if (pointVarIndex > MAX_VARS - 1)
        {
          vtkErrorMacro("Exceeded number of Point vars.");
          return 0;
        }
        this->Internals->PointVars[pointVarIndex] = aVar;
        vtkDebugMacro("Adding var " << aVar.Name << " to PointVars");
      }
    }
    // 2D variables
    else if (varType == 2)
    {
      vtkDebugMacro("check for " << aVar.Name << ".");
      if (isCellData)
      {
        cellVarIndex++;
        if (cellVarIndex > MAX_VARS - 1)
        {
          vtkDebugMacro("Exceeded number of cell vars.");
          return 0;
        }
        this->Internals->CellVars[cellVarIndex] = aVar;
        vtkDebugMacro("Adding var " << aVar.Name << " to CellVars");
      }
      else if (isPointData)
      {
        pointVarIndex++;
        if (pointVarIndex > MAX_VARS - 1)
        {
          vtkErrorMacro("Exceeded number of Point vars.");
          return 0;
        }
        this->Internals->PointVars[pointVarIndex] = aVar;
        vtkDebugMacro("Adding var " << aVar.Name << " to PointVars");
      }
    }
  }

  // check if domain var is defined and how many domains are available
  for (int var = 0; var < pointVarIndex + 1; var++)
  {
    if (!strcmp(this->Internals->PointVars[var].Name, this->DomainVarName.c_str()))
    {
      this->HaveDomainVariable = true;
    }
  }
  for (int var = 0; var < cellVarIndex + 1; var++)
  {
    if (!strcmp(this->Internals->CellVars[var].Name, this->DomainVarName.c_str()))
    {
      this->HaveDomainVariable = true;
    }
  }

  // prepare data structure and read in names
  std::string filename = this->PerformanceDataFile + "0000";
  vtksys::ifstream file(filename.c_str());
  if (file.good())
  {
    this->HaveDomainData = true;
  }

  if (this->SupportDomainData())
  {
    vtkDebugMacro("Found Domain Data and loading it.");
    this->BuildDomainArrays = true;
  }

  this->NumberOfPointVars = pointVarIndex + 1;
  this->NumberOfCellVars = cellVarIndex + 1;
  this->NumberOfDomainVars = domainVarIndex + 1;
  this->NumberAllCells = this->NumberOfCells;
  this->NumberAllPoints = this->NumberOfPoints;

  return 1;
}

//----------------------------------------------------------------------------
// Build the selection Arrays for points and cells in the GUI.
//----------------------------------------------------------------------------
int vtkCDIReader::BuildVarArrays()
{
  vtkDebugMacro("In vtkCDIReader::BuildVarArrays");

  if (!this->FileName.empty())
  {
    if (!this->GetVars())
    {
      return 0;
    }

    vtkDebugMacro("NumberOfCellVars: " << this->NumberOfCellVars
                                       << " NumberOfPointVars: " << this->NumberOfPointVars);
    if (this->NumberOfCellVars == 0)
    {
      vtkDebugMacro("No cell variables found!");
    }

    for (int var = 0; var < this->NumberOfPointVars; var++)
    {
      this->PointDataArraySelection->EnableArray(this->Internals->PointVars[var].Name);
      vtkDebugMacro("Adding Point var: " << this->Internals->PointVars[var].Name);
    }

    for (int var = 0; var < this->NumberOfCellVars; var++)
    {
      vtkDebugMacro("Adding cell var: " << this->Internals->CellVars[var].Name);
      this->CellDataArraySelection->EnableArray(this->Internals->CellVars[var].Name);
    }

    for (int var = 0; var < this->NumberOfDomainVars; var++)
    {
      vtkDebugMacro("Adding domain var: " << this->Internals->DomainVars[var].c_str());
      this->DomainDataArraySelection->EnableArray(this->Internals->DomainVars[var].c_str());
    }
  }
  else
  {
    vtkDebugMacro("No Filename yet set.");
  }

  vtkDebugMacro("Leaving vtkCDIReader::BuildVarArrays");
  return 1;
}

//----------------------------------------------------------------------------
//  Read the data from the ncfile, allocate the geometry and create the
//  vtk data structures for points and cells.
//----------------------------------------------------------------------------
int vtkCDIReader::ReadAndOutputGrid(bool init)
{
  vtkDebugMacro("In vtkCDIReader::ReadAndOutputGrid");

  if (this->ProjectionMode == projection::SPHERICAL)
  {
    if (!this->AllocSphereGeometry())
    {
      return 0;
    }
  }
  else
  {
    if (!this->AllocLatLonGeometry())
    {
      return 0;
    }

    if (this->ProjectionMode == projection::CASSINI)
    {
      if (!this->Wrap(2))
      {
        return 0;
      }
    }
    else
    {
      if (!this->Wrap(1))
      {
        return 0;
      }
    }
  }

  if ((this->ProjectionMode == projection::CYLINDRICAL_EQUIDISTANT) ||
    (this->ProjectionMode == projection::CASSINI))
  {
    this->AddMaskHalo();
    this->AddClonClatHalo();
  }
  this->OutputPoints(init);
  this->OutputCells(init);

  vtkDebugMacro("Leaving vtkCDIReader::ReadAndOutputGrid");

  return 1;
}

//----------------------------------------------------------------------------
// Mirrors the triangle mesh in z direction
//----------------------------------------------------------------------------
int vtkCDIReader::MirrorMesh()
{
  for (int i = 0; i < this->NumberLocalPoints; i++)
  {
    this->PointZ[i] = (this->PointZ[i] * (-1.0));
  }

  return 1;
}

//----------------------------------------------------------------------------
void vtkCDIReader::RemoveDuplicates(
  double* pointLon, double* pointLat, int temp_nbr_vertices, int* triangle_list, int* nbr_cells)
{
  struct PointWithIndex* sort_array = new PointWithIndex[temp_nbr_vertices];

  for (int i = 0; i < temp_nbr_vertices; ++i)
  {
    double curr_lon, curr_lat;
    double threshold = (vtkMath::Pi() / 2.0) - 1e-4;
    curr_lon = ((double*)pointLon)[i];
    curr_lat = ((double*)pointLat)[i];

    while (curr_lon < 0.0)
    {
      curr_lon += 2 * vtkMath::Pi();
    }
    while (curr_lon >= vtkMath::Pi())
    {
      curr_lon -= 2 * vtkMath::Pi();
    }

    if (curr_lat > threshold)
    {
      curr_lon = 0.0;
    }
    else if (curr_lat < (-1.0 * threshold))
    {
      curr_lon = 0.0;
    }

    sort_array[i].Pt.Lon = curr_lon;
    sort_array[i].Pt.Lat = curr_lat;
    sort_array[i].Idx = i;
  }

  qsort(sort_array, temp_nbr_vertices, sizeof(*sort_array), ::ComparePointWithIndex);
  triangle_list[sort_array[0].Idx] = 1;

  int last_unique_idx = sort_array[0].Idx;
  for (int i = 1; i < temp_nbr_vertices; ++i)
  {
    if (::ComparePointWithIndex(sort_array + i - 1, sort_array + i))
    {
      triangle_list[sort_array[i].Idx] = 1;
      last_unique_idx = sort_array[i].Idx;
    }
    else
    {
      triangle_list[sort_array[i].Idx] = -last_unique_idx;
    }
  }

  int new_nbr_vertices = 0;
  for (int i = 0; i < temp_nbr_vertices; ++i)
  {
    if (triangle_list[i] == 1)
    {
      pointLon[new_nbr_vertices] = pointLon[i];
      pointLat[new_nbr_vertices] = pointLat[i];
      triangle_list[i] = new_nbr_vertices;
      new_nbr_vertices++;
    }
  }

  for (int i = 0; i < temp_nbr_vertices; ++i)
  {
    if (triangle_list[i] <= 0)
    {
      triangle_list[i] = triangle_list[-triangle_list[i]];
    }
  }

  nbr_cells[0] = temp_nbr_vertices;
  nbr_cells[1] = new_nbr_vertices;
  delete[] sort_array;
}

//----------------------------------------------------------------------------
// Construct grid geometry
//----------------------------------------------------------------------------
int vtkCDIReader::ConstructGridGeometry()
{
  vtkDebugMacro("Starting grid reconstruction ...");
  if (this->NumberOfCellVars == 0 && this->NumberOfPointVars == 0)
  {
    vtkErrorMacro("No cell variables - can't construct grid");
    return 0;
  }

  this->NumberLocalCells = this->GetPartitioning(this->Piece, this->NumPieces, this->NumberOfCells,
    this->PointsPerCell, this->BeginPoint, this->EndPoint, this->BeginCell, this->EndCell);

  int size = this->NumberLocalCells * this->PointsPerCell;
  int size2 = this->NumberAllCells * this->PointsPerCell;
  std::vector<double> cLonVertices;
  std::vector<double> cLatVertices;
  if (size > cLonVertices.max_size())
  {
    vtkErrorMacro("Too many points to construct geometry.");
    return 0;
  }
  cLonVertices.resize(size);
  cLatVertices.resize(size);
  this->DepthVar.resize(this->MaximumNVertLevels);

  vtkDebugMacro("Start reading Vertices");
  try
  {
    gridInqXboundsPart(Internals->Grids.at(this->GridID).GridID,
      (this->BeginCell * this->PointsPerCell), size, cLonVertices.data());
    gridInqYboundsPart(Internals->Grids.at(this->GridID).GridID,
      (this->BeginCell * this->PointsPerCell), size, cLatVertices.data());
  }
  catch (const std::out_of_range& oor)
  {
    vtkErrorMacro(
      "Out of Range error trying to get the grid id for reading vertices: " << oor.what());
    return 0;
  }
  vtkDebugMacro("Done reading Vertices");
  vtkDebugMacro("Getting vertical axis" << this->ZAxisID << " expecting up to "
                                        << this->MaximumNVertLevels << " levels.");
  zaxisInqLevels(this->ZAxisID, this->DepthVar.data());
  vtkDebugMacro("Got vertical axis" << this->ZAxisID);
  char units[CDI_MAX_NAME];
  this->OrigConnections.resize(size);
  int new_cells[2];
  try
  {
    if (this->ProjectionMode != projection::CATALYST)
    {
      gridInqXunits(this->Internals->Grids.at(this->GridID).GridID, units);
      if (strncmp(units, "degree", 6) == 0)
      {
        for (int i = 0; i < size; i++)
        {
          cLonVertices[i] = vtkMath::RadiansFromDegrees(cLonVertices[i]);
        }
      }
      gridInqYunits(this->Internals->Grids.at(this->GridID).GridID, units);
      if (strncmp(units, "degree", 6) == 0)
      {
        for (int i = 0; i < size; i++)
        {
          cLatVertices[i] = vtkMath::RadiansFromDegrees(cLatVertices[i]);
        }
      }
    }
  }
  catch (const std::out_of_range& oor)
  {
    vtkErrorMacro("Out of Range error trying to get the grid id for getting the coordinate units "
                  "for projecting: "
      << oor.what());
    return 0;
  }

  // check for duplicates in the Point list and update the triangle list
  vtkDebugMacro("Removing duplicates for clon/clat, size = " << size);

  this->RemoveDuplicates(
    cLonVertices.data(), cLatVertices.data(), size, &this->OrigConnections[0], new_cells);
  vtkDebugMacro("Removed duplicates for clon/clat");
  this->NumberLocalCells = new_cells[0] / this->PointsPerCell;
  this->NumberLocalPoints = new_cells[1];
  if (this->NumberOfPoints and this->NumberOfPoints != new_cells[1])
  {
    vtkDebugMacro("ConstructGridGeometry: Changing number of points from  "
      << this->NumberOfPoints << " to " << new_cells[1]);
  }

  this->NumberOfPoints = new_cells[1];

  this->ModNumPoints = (int)floor(this->NumberLocalPoints * (this->Bloat * this->Bloat));
  this->ModNumCells = (int)floor(this->NumberLocalCells * (this->Bloat));

  this->PointMap.resize((size_t)floor(this->NumberOfPoints * (this->Bloat * this->Bloat)));
  this->CellMap.resize((size_t)floor(this->NumberOfCells * this->Bloat));

  this->PointX.resize(this->ModNumPoints);
  this->PointY.resize(this->ModNumPoints);
  this->PointZ.resize(this->ModNumPoints);

  // now get the individual coordinates out of the clon/clat vertices
  for (int i = 0; i < this->NumberLocalPoints; i++)
  {
    projection::longLatToCartesian(cLonVertices[i], cLatVertices[i], &this->PointX[i],
      &this->PointY[i], &this->PointZ[i], this->ProjectionMode);
  }

  // mirror the mesh if needed
  if (this->ProjectionMode == projection::SPHERICAL)
  {
    this->MirrorMesh();
  }
  this->GridReconstructed = true;
  this->ReconstructNew = false;

  // if we run with data decomposition, we need to know the mapping of points
  this->VertexIds.resize(size);
  std::vector<int> vertex_ids2;
  if (size2 > vertex_ids2.max_size())
  {
    vtkErrorMacro("Too many points to construct geometry.");
    return 0;
  }
  vertex_ids2.resize(size2);
#if VTK_MODULE_ENABLE_VTK_ParallelMPI
  if (this->Decomposition)
  {
    if (this->Piece == 0)
    {
      int new_cells2[2];
      std::vector<double> clon_vert2(size2);
      std::vector<double> clat_vert2(size2);
      try
      {
        gridInqXboundsPart(
          this->Internals->Grids.at(this->GridID).GridID, 0, size2, clon_vert2.data());
        gridInqYboundsPart(
          this->Internals->Grids.at(this->GridID).GridID, 0, size2, clat_vert2.data());

        gridInqXunits(this->Internals->Grids.at(this->GridID).GridID, units);
        if (strncmp(units, "degree", 6) == 0)
        {
          for (int i = 0; i < size2; i++)
          {
            clon_vert2[i] = vtkMath::RadiansFromDegrees(clon_vert2[i]);
          }
        }

        gridInqYunits(this->Internals->Grids.at(this->GridID).GridID, units);
        if (strncmp(units, "degree", 6) == 0)
        {
          for (int i = 0; i < size2; i++)
          {
            clat_vert2[i] = vtkMath::RadiansFromDegrees(clat_vert2[i]);
          }
        }
      }
      catch (const std::out_of_range& oor)
      {
        vtkErrorMacro(
          "Out of Range error trying to get the grid id for converting lat/lon in parallel: "
          << oor.what());
        return 0;
      }

      vtkDebugMacro("Removing duplicates for clon/clat2");

      this->RemoveDuplicates(
        clon_vert2.data(), clat_vert2.data(), size2, vertex_ids2.data(), new_cells2);
      for (int i = 1; i < this->NumPieces; i++)
      {
        this->Controller->Send(vertex_ids2.data(), size2, i, 101);
      }
    }
    else
    {
      this->Controller->Receive(vertex_ids2.data(), size2, 0, 101);

      for (int i = this->BeginPoint; i < (this->EndPoint + 1); i++)
      {
        this->VertexIds[i - this->BeginPoint] = vertex_ids2[i];
      }
    }

    this->SetupPointConnectivity();
  }
#endif

  this->CurrentExtraPoint = this->NumberLocalPoints;
  this->CurrentExtraCell = this->NumberLocalCells;

  vtkDebugMacro("Grid Reconstruction complete...");
  return 1;
}

//----------------------------------------------------------------------------
// Allocate into sphere view of geometry
// This is work in progress, but as almost all variables are cell based, it
// is not that urgent.
//----------------------------------------------------------------------------
void vtkCDIReader::SetupPointConnectivity()
{
  this->Internals->PointsExpectedFromProcessesLengths = vtkSmartPointer<vtkIdTypeArray>::New();
  this->Internals->PointsExpectedFromProcessesLengths->SetNumberOfTuples(this->NumPieces);
  this->Internals->PointsExpectedFromProcessesOffsets = vtkSmartPointer<vtkIdTypeArray>::New();
  this->Internals->PointsExpectedFromProcessesOffsets->SetNumberOfTuples(this->NumPieces);
  this->Internals->PointsToSendToProcesses = vtkSmartPointer<vtkIdTypeArray>::New();
  this->Internals->PointsToSendToProcessesLengths = vtkSmartPointer<vtkIdTypeArray>::New();
  this->Internals->PointsToSendToProcessesLengths->SetNumberOfTuples(this->NumPieces);
  this->Internals->PointsToSendToProcessesOffsets = vtkSmartPointer<vtkIdTypeArray>::New();
  this->Internals->PointsToSendToProcessesOffsets->SetNumberOfTuples(this->NumPieces);
}

//----------------------------------------------------------------------------
// Allocate into sphere view of geometry
//----------------------------------------------------------------------------
int vtkCDIReader::AllocSphereGeometry()
{
  vtkDebugMacro("In AllocSphereGeometry...");

  if (!this->GridReconstructed || this->ReconstructNew)
  {
    this->ConstructGridGeometry();
  }

  if (this->ShowMultilayerView)
  {
    this->MaximumCells = this->NumberLocalCells * this->MaximumNVertLevels;
    this->MaximumPoints = this->NumberLocalPoints * (this->MaximumNVertLevels + 1);
  }
  else
  {
    this->MaximumCells = this->NumberLocalCells;
    this->MaximumPoints = this->NumberLocalPoints;
  }

  if (this->ShowClonClat)
  {
    this->LoadClonClatVars();
  }
  this->CheckForMaskData();
  vtkDebugMacro("Leaving AllocSphereGeometry...");
  return 1;
}

//----------------------------------------------------------------------------
// Allocate the lat/lon or Cassini projection of geometry.
//----------------------------------------------------------------------------
int vtkCDIReader::AllocLatLonGeometry()
{
  vtkDebugMacro("In AllocLatLonGeometry...");
  if (this->NumberOfCellVars == 0 && this->NumberOfPointVars == 0)
  {
    vtkErrorMacro("No cell or point variables - can't construct grid");
    return 0;
  }

  if (!this->GridReconstructed || this->ReconstructNew)
  {
    this->ConstructGridGeometry();
  }

  this->ModConnections.resize(this->ModNumCells * this->PointsPerCell);

  if (this->ShowMultilayerView)
  {
    this->MaximumCells = this->CurrentExtraCell * this->MaximumNVertLevels;
    this->MaximumPoints = this->CurrentExtraPoint * (this->MaximumNVertLevels + 1);
  }
  else
  {
    this->MaximumPoints = this->CurrentExtraPoint;
    this->MaximumCells = this->CurrentExtraCell;
  }

  if (this->ShowClonClat)
  {
    this->LoadClonClatVars();
  }
  this->CheckForMaskData();
  vtkDebugMacro("Leaving AllocLatLonGeometry...");
  return 1;
}

//----------------------------------------------------------------------------
// Construct grid geometry
//----------------------------------------------------------------------------
int vtkCDIReader::LoadClonClatVars()
{
  vtkDebugMacro("In LoadClonClatVars...");

  std::vector<double> cLon_l(this->NumberLocalCells);
  std::vector<double> cLat_l(this->NumberLocalCells);

  gridInqXvalsPart(this->Internals->Grids.at(this->GridID).GridID, this->BeginCell,
    this->NumberLocalCells, cLon_l.data());
  gridInqYvalsPart(this->Internals->Grids.at(this->GridID).GridID, this->BeginCell,
    this->NumberLocalCells, cLat_l.data());

  char units[CDI_MAX_NAME];

  try
  {
    gridInqXunits(this->Internals->Grids.at(this->GridID).GridID, units);
    if (strncmp(units, "degree", 6) == 0)
    {
      for (int i = 0; i < this->NumberLocalCells; i++)
      {
        cLon_l[i] = vtkMath::RadiansFromDegrees(cLon_l[i]);
      }
    }
    gridInqYunits(this->Internals->Grids.at(this->GridID).GridID, units);
    if (strncmp(units, "degree", 6) == 0)
    {
      for (int i = 0; i < this->NumberLocalCells; i++)
      {
        cLat_l[i] = vtkMath::RadiansFromDegrees(cLat_l[i]);
      }
    }
  }
  catch (const std::out_of_range& oor)
  {
    vtkErrorMacro(
      "Out of Range error trying to get the grid id for converting lat/lon in LoadClonClatVars: "
      << oor.what());
    return 0;
  }

  if (this->ShowMultilayerView)
  {
    int tmp = this->MaximumCells * this->Bloat;
    this->CLon.resize(tmp);
    this->CLat.resize(tmp);

    for (int j = 0; j < this->NumberLocalCells; j++)
    {
      for (int levelNum = 0; levelNum < this->MaximumNVertLevels; levelNum++)
      {
        int i = j * this->MaximumNVertLevels;
        this->CLon[i + levelNum] = static_cast<double>(cLon_l[j]);
        this->CLat[i + levelNum] = static_cast<double>(cLat_l[j]);
      }
    }
  }
  else
  {
    int tmp = this->NumberLocalCells * this->Bloat;
    this->CLon.resize(tmp);
    this->CLat.resize(tmp);

    for (int j = 0; j < this->NumberLocalCells; j++)
    {
      this->CLon[j] = static_cast<double>(cLon_l[j]);
      this->CLat[j] = static_cast<double>(cLat_l[j]);
    }
  }

  this->AddCoordinateVars = true;
  vtkDebugMacro("Out LoadClonClatVars...");
  return 1;
}

//----------------------------------------------------------------------------
int vtkCDIReader::AddClonClatHalo()
{
  if ((this->AddCoordinateVars) && (this->NumberLocalCells < this->CurrentExtraCell))
  {
    if (this->ShowMultilayerView)
    {
      // put out data for extra cells
      for (int j = this->NumberLocalCells; j < this->CurrentExtraCell; j++)
      {
        for (int levelNum = 0; levelNum < this->MaximumNVertLevels; levelNum++)
        {
          int l = j * this->MaximumNVertLevels;
          int k = this->MaximumNVertLevels * this->CellMap[j - this->NumberLocalCells];
          this->CLon[l + levelNum] = static_cast<int>(CLon[k + levelNum]);
          this->CLat[l + levelNum] = static_cast<int>(CLat[k + levelNum]);
        }
      }
    }
    else
    {
      // put out data for extra cells
      for (int j = this->NumberLocalCells; j < this->CurrentExtraCell; j++)
      {
        int k = this->CellMap[j - this->NumberLocalCells];
        this->CLon[j] = static_cast<int>(CLon[k]);
        this->CLat[j] = static_cast<int>(CLat[k]);
      }
    }

    return 1;
  }

  return 0;
}

//----------------------------------------------------------------------------
//  Read in Missing value mask. We probably should not read this, but just fetch it, but hell,
//  whatever.
//----------------------------------------------------------------------------
int vtkCDIReader::CheckForMaskData()
{
  int numVars = this->NumberOfCellVars;
  this->GotMask = false;
  int mask_pos = 0;

  std::string mask_name;
  if (this->PointsPerCell == 3)
    mask_name = "wet_c";
  if (this->PointsPerCell == 4)
    mask_name = "wet_e";
  if (this->PointsPerCell == 6)
    mask_name = "wet_v";

  for (int i = 0; i < numVars; i++)
  {
    if (!strcmp(this->Internals->CellVars[i].Name, this->MaskingVarname.c_str()))
    {
      if (this->MaskingVarname.empty())
      {
        vtkWarningMacro(<< "Found empty var at position " << i << " of Internals->CellVars ");
        break;
      }

      this->GotMask = true;
      mask_pos = i;
      break;
    }
  }
  if (this->GotMask == false)
  {
    this->MaskingVarname = mask_name;
    for (int i = 0; i < numVars; i++)
    {
      if (!strcmp(this->Internals->CellVars[i].Name, this->MaskingVarname.c_str()))
      {
        if (this->MaskingVarname.empty())
        {
          vtkWarningMacro(<< "Found empty var at position " << i << " of Internals->CellVars ");
          break;
        }
        this->GotMask = true;
        mask_pos = i;
        break;
      }
    }
  }

  if (this->GotMask)
  {
    const double maskVal = this->UseCustomMaskValue
      ? this->CustomMaskValue
      : vlistInqVarMissval(
          this->Internals->DataFile.getVListID(), this->Internals->CellVars[mask_pos].VarID);

    cdi_tools::CDIVar* cdiVar = &(this->Internals->CellVars[mask_pos]);
    if (this->ShowMultilayerView)
    {
      this->CellMask.resize(this->MaximumCells * this->Bloat);
      float* dataTmpMask = new float[this->MaximumCells * sizeof(float)];
      CHECK_NEW(dataTmpMask);

      cdi_set_cur(cdiVar, 0, 0);
      cdi_tools::cdi_get_part<float>(cdiVar, this->BeginCell, this->NumberLocalCells, dataTmpMask,
        this->MaximumNVertLevels, this->Grib);
      vtkDebugMacro("Done with read of 3d Mask data");

      // readjust the data
      for (int j = 0; j < this->NumberLocalCells; j++)
      {
        for (int levelNum = 0; levelNum < this->MaximumNVertLevels; levelNum++)
        {
          int i = j * this->MaximumNVertLevels;
          this->CellMask[i + levelNum] =
            (dataTmpMask[j + (levelNum * this->NumberLocalCells)] == maskVal);
        }
      }

      delete[] dataTmpMask;
      vtkDebugMacro("Got data for missing value mask (3D)");
    }
    else
    {
      this->CellMask.resize(this->NumberLocalCells * this->Bloat);
      float* dataTmpMask = new float[this->NumberLocalCells];

      cdi_set_cur(cdiVar, 0, this->VerticalLevelSelected);
      cdi_tools::cdi_get_part<float>(
        cdiVar, this->BeginCell, this->NumberLocalCells, dataTmpMask, 1, this->Grib);

      // readjust the data
      for (int j = 0; j < this->NumberLocalCells; j++)
      {
        this->CellMask[j] = (dataTmpMask[j] == maskVal);
      }

      delete[] dataTmpMask;
      vtkDebugMacro("Got data for missing value mask (2D)");
    }
    this->GotMask = true;
  }

  return 1;
}

//----------------------------------------------------------------------------
//  Add halo to the land sea mask (for points on 180 deg line / ... )
//----------------------------------------------------------------------------
int vtkCDIReader::AddMaskHalo()
{
  if ((this->GotMask) && (this->NumberLocalCells < this->CurrentExtraCell))
  {
    if (this->ShowMultilayerView)
    {
      // put out data for extra cells
      for (int j = this->NumberLocalCells; j < this->CurrentExtraCell; j++)
      {
        for (int levelNum = 0; levelNum < this->MaximumNVertLevels; levelNum++)
        {
          int l = j * this->MaximumNVertLevels;
          int k = this->MaximumNVertLevels * this->CellMap[j - this->NumberLocalCells];
          this->CellMask[l + levelNum] = (this->CellMask[k + levelNum]);
        }
      }
    }
    else
    {
      // put out data for extra cells
      for (int j = this->NumberLocalCells; j < this->CurrentExtraCell; j++)
      {
        int k = this->CellMap[j - this->NumberLocalCells];
        this->CellMask[j] = this->CellMask[k];
      }
    }

    return 1;
  }

  return 0;
}

//----------------------------------------------------------------------------
//  Load domain (performance data) variables and integrate them as
//  cell vars. //  Check http://www.kitware.com/blog/home/post/817 if it
//  is possible to adapt to this technique.
//----------------------------------------------------------------------------
bool vtkCDIReader::BuildDomainCellVars()
{
  this->DomainCellVar.resize(this->NumberOfCells * this->NumberOfDomainVars);
  std::vector<double> domainTMP(this->NumberOfCells);
  double val = 0;
  int mask_pos = 0;
  int numVars = vlistNvars(this->Internals->DataFile.getVListID());

  for (int i = 0; i < numVars; i++)
  {
    if (!strcmp(this->Internals->CellVars[i].Name, this->DomainVarName.c_str()))
    {
      mask_pos = i;
    }
  }

  cdi_tools::CDIVar* cdiVar = &(this->Internals->CellVars[mask_pos]);
  cdi_set_cur(cdiVar, 0, 0);
  cdi_tools::cdi_get_part<double>(
    cdiVar, this->BeginCell, this->NumberLocalCells, domainTMP.data(), 1, this->Grib);

  for (int j = 0; j < this->NumberOfDomainVars; j++)
  {
    vtkNew<vtkDoubleArray> domainVar;
    for (int k = 0; k < this->NumberOfCells; k++)
    {
      val = this->DomainVarDataArray->GetArray(j)->GetComponent(domainTMP[k], 0l);
      this->DomainCellVar[k + (j * this->NumberOfCells)] = val;
    }
    domainVar->SetArray(&(this->DomainCellVar[j * this->NumberOfCells]), this->NumberLocalCells, 0,
      vtkDoubleArray::VTK_DATA_ARRAY_FREE);
    domainVar->SetName(this->Internals->DomainVars[j].c_str());
    this->Output->GetCellData()->AddArray(domainVar);
  }

  vtkDebugMacro("Built cell vars from domain data");
  return 1;
}

//------------------------------------------------------------------------------
//  Add a "mirror point" X -- a point on the opposite side of the lat/lon projection.
//------------------------------------------------------------------------------
int vtkCDIReader::AddMirrorPointX(int index, double dividerX, double offset)
{
  vtkDebugMacro("In AddMirrorPoint X...");
  double x = this->PointX[index];
  double y = this->PointY[index];

  // add on east
  if (x < dividerX)
  {
    x += offset;
  }
  else
  {
    // add on west
    x -= offset;
  }

  this->PointX[this->CurrentExtraPoint] = x;
  this->PointY[this->CurrentExtraPoint] = y;

  size_t mirrorPoint = this->CurrentExtraPoint;

  // record mapping
  this->PointMap[this->CurrentExtraPoint - this->NumberLocalPoints] = index;
  this->CurrentExtraPoint++;

  vtkDebugMacro("Out AddMirrorPoint X...");
  return static_cast<int>(mirrorPoint);
}

//------------------------------------------------------------------------------
//  Add a "mirror point" Y -- a point on the opposite side of the Cassini projection.
//------------------------------------------------------------------------------
int vtkCDIReader::AddMirrorPointY(int index, double dividerY, double offset)
{
  vtkDebugMacro("In AddMirrorPoint Y...");
  double x = this->PointX[index];
  double y = this->PointY[index];

  // add on south
  if (y < dividerY)
  {
    y += offset;
  }
  else
  {
    // add on north
    y -= offset;
  }

  this->PointX[this->CurrentExtraPoint] = x;
  this->PointY[this->CurrentExtraPoint] = y;

  size_t mirrorPoint = this->CurrentExtraPoint;

  // record mapping
  this->PointMap[this->CurrentExtraPoint - this->NumberLocalPoints] = index;
  this->CurrentExtraPoint++;

  vtkDebugMacro("Out AddMirrorPoint Y...");
  return static_cast<int>(mirrorPoint);
}

//----------------------------------------------------------------------------
// Elimate Wrap for lon/lat Projection   1 = x-Axis, 2 = Y-Axis
//----------------------------------------------------------------------------
int vtkCDIReader::Wrap(int axis)
{
  vtkDebugMacro("In Wrapping...");

  if ((this->WrapOn) &&
    ((this->ProjectionMode == projection::CYLINDRICAL_EQUIDISTANT) ||
      (this->ProjectionMode == projection::CASSINI)))
  {
    double xyLength = 2 * vtkMath::Pi();
    double xyCenter = 0.0;
    const double toleranceXY = 1.0;

    // For each cell, examine vertices
    // Add new points and cells where needed to account for wraparound.
    for (size_t j = 0; j < this->NumberLocalCells; j++)
    {
      int* conns = &this->OrigConnections[j * this->PointsPerCell];
      int* modConns = &this->ModConnections[j * this->PointsPerCell];

      // Determine if we are wrapping in X direction
      size_t lastk = this->PointsPerCell - 1;
      bool xyWrap = false;
      for (size_t k = 0; k < this->PointsPerCell; k++)
      {
        if ((std::abs(this->PointX[conns[k]] - this->PointX[conns[lastk]]) > toleranceXY) &&
          (axis == 1))
        {
          xyWrap = true;
        }
        if ((std::abs(this->PointY[conns[k]] - this->PointY[conns[lastk]]) > toleranceXY) &&
          (axis == 2))
        {
          xyWrap = true;
        }
        lastk = k;
      }

      // If we wrapped in X direction, modify cell and add mirror cell
      if (xyWrap)
      {
        // first point is anchor it doesn't move
        double anchorX = this->PointX[conns[0]];
        double anchorY = this->PointY[conns[0]];

        modConns[0] = conns[0];
        int numExtraPoints = 0;

        // modify existing cell, so it doesn't wrap
        // move points to one side
        for (size_t k = 1; k < this->PointsPerCell; k++)
        {
          int neigh = conns[k];

          // add a new point, figure out east or west
          if ((axis == 1) && (std::abs(this->PointX[neigh] - anchorX) > toleranceXY))
          {
            modConns[k] = this->AddMirrorPointX(neigh, anchorX, xyLength);
            numExtraPoints++;
          }
          else if ((axis == 2) && (std::abs(this->PointY[neigh] - anchorY) > toleranceXY))
          {
            modConns[k] = this->AddMirrorPointY(neigh, anchorY, xyLength);
            numExtraPoints++;
          }
          else
          {
            // use existing kth point
            modConns[k] = neigh;
          }
        }

        // move addedConns to this->ModConnections extra cells area
        int* addedConns = &this->ModConnections[this->CurrentExtraCell * this->PointsPerCell];

        // add a mirroring cell to other side
        // add mirrored anchor first
        if (axis == 1)
        {
          addedConns[0] = this->AddMirrorPointX(conns[0], xyCenter, xyLength);
          anchorX = this->PointX[addedConns[0]];
        }
        else if (axis == 2)
        {
          addedConns[0] = this->AddMirrorPointY(conns[0], xyCenter, xyLength);
          anchorY = this->PointY[addedConns[0]];
        }
        numExtraPoints++;

        // add mirror cell points if needed
        for (size_t k = 1; k < this->PointsPerCell; k++)
        {
          int neigh = conns[k];

          // add a new point for neighbor, figure out east or west
          if ((axis == 1) && (std::abs(this->PointX[neigh] - anchorX) > toleranceXY))
          {
            addedConns[k] = this->AddMirrorPointX(neigh, anchorX, xyLength);
            numExtraPoints++;
          }
          else if ((axis == 2) && (std::abs(this->PointY[neigh] - anchorY) > toleranceXY))
          {
            addedConns[k] = this->AddMirrorPointY(neigh, anchorY, xyLength);
            numExtraPoints++;
          }
          else
          {
            // use existing kth point
            addedConns[k] = neigh;
          }
        }
        this->CellMap[this->CurrentExtraCell - this->NumberLocalCells] = j;
        this->CurrentExtraCell++;
      }
      else
      {
        // just add cell "as is" to this->ModConnections
        for (size_t k = 0; k < this->PointsPerCell; k++)
        {
          modConns[k] = conns[k];
        }
      }
      if (this->CurrentExtraCell > this->ModNumCells)
      {
        vtkErrorMacro(<< "Exceeded storage for extra cells!");
        return (0);
      }
      if (this->CurrentExtraPoint > this->ModNumPoints)
      {
        vtkErrorMacro(<< "Exceeded storage for extra points!");
        return (0);
      }
    }

    if (!this->ShowMultilayerView)
    {
      this->MaximumCells = static_cast<int>(this->CurrentExtraCell);
      this->MaximumPoints = static_cast<int>(this->CurrentExtraPoint);
      vtkDebugMacro(<< "elim xwrap: singlelayer: setting this->MaximumPoints to "
                    << this->MaximumPoints);
    }
    else
    {
      this->MaximumCells = static_cast<int>(this->CurrentExtraCell * this->MaximumNVertLevels);
      this->MaximumPoints =
        static_cast<int>(this->CurrentExtraPoint * (this->MaximumNVertLevels + 1));
      vtkDebugMacro(<< "elim xwrap: multilayer: setting this->MaximumPoints to "
                    << this->MaximumPoints);
    }

    vtkDebugMacro("Out Wrapping...");
    return 1;
  }
  else
  {
    for (int j = 0; j < this->NumberLocalCells; j++)
    {
      int* conns = &this->OrigConnections[j * this->PointsPerCell];
      int* modConns = &this->ModConnections[j * this->PointsPerCell];
      int lastk = this->PointsPerCell - 1;
      bool xyWrap = false;

      for (int k = 0; k < this->PointsPerCell; k++)
      {
        if ((abs(this->PointX[conns[k]] - this->PointX[conns[lastk]]) > 1.0) && (axis == 1))
        {
          xyWrap = true;
        }
        if ((abs(this->PointY[conns[k]] - this->PointY[conns[lastk]]) > 1.0) && (axis == 2))
        {
          xyWrap = true;
        }

        lastk = k;
      }

      if (xyWrap)
      {
        for (int k = 0; k < this->PointsPerCell; k++)
        {
          modConns[k] = 0;
        }
      }
      else
      {
        for (int k = 0; k < this->PointsPerCell; k++)
        {
          modConns[k] = conns[k];
        }
      }
    }
    vtkDebugMacro("Out Wrapping...");
    return 1;
  }
}

//----------------------------------------------------------------------------
//  Add points to vtk data structures
//----------------------------------------------------------------------------
void vtkCDIReader::OutputPoints(bool init)
{
  vtkDebugMacro("In OutputPoints...");
  float layerThicknessScaleFactor = 5000.0;
  vtkSmartPointer<vtkPoints> points;
  float adjustedLayerThickness = (this->LayerThickness / layerThicknessScaleFactor);

  if (this->InvertZAxis)
  {
    adjustedLayerThickness = -1.0 * (this->LayerThickness / layerThicknessScaleFactor);
  }

  vtkDebugMacro("OutputPoints: this->MaximumPoints: "
    << this->MaximumPoints << " this->MaximumNVertLevels: " << this->MaximumNVertLevels
    << " LayerThickness: " << this->LayerThickness
    << " ShowMultilayerView: " << this->ShowMultilayerView);
  if (init)
  {
    points = vtkSmartPointer<vtkPoints>::New();
    points->Allocate(this->MaximumPoints, this->MaximumPoints);
    this->Output->SetPoints(points);
  }
  else
  {
    points = this->Output->GetPoints();
    points->Initialize();
    points->Allocate(this->MaximumPoints, this->MaximumPoints);
  }

  if (this->DepthVar.empty())
  {
    vtkDebugMacro("OutputPoints: this->MaximumPoints: "
      << this->MaximumPoints << " this->MaximumNVertLevels: " << this->MaximumNVertLevels
      << " LayerThickness: " << this->LayerThickness
      << " ShowMultilayerView: " << this->ShowMultilayerView);
    return; // We don't have any data anyways.
  }

  double sx, sy, sz;
  get_scaling(this->ProjectionMode, this->ShowMultilayerView,
    this->DepthVar[this->VerticalLevelSelected], adjustedLayerThickness, sx, sy, sz);

  for (int j = 0; j < this->CurrentExtraPoint; j++)
  {
    double x = this->PointX[j] * sx;
    double y = this->PointY[j] * sy;
    double z = this->PointZ[j] * sz;

    if (!this->ShowMultilayerView)
    {
      points->InsertNextPoint(x, y, z);
    }
    else
    {
      double rho = 0.0, rholevel = 0.0, theta = 0.0, phi = 0.0;
      int retval = -1;

      if (this->ProjectionMode == projection::SPHERICAL)
      {
        if ((x != 0.0) || (y != 0.0) || (z != 0.0))
        {
          retval = projection::cartesianToSpherical(x, y, z, &rho, &phi, &theta);
          if (!retval)
          {
            retval = projection::sphericalToCartesian(
              rho + this->Layer0Offset * adjustedLayerThickness, phi, theta, &x, &y, &z);
          }
        }
      }

      if (this->ProjectionMode != projection::SPHERICAL)
      {
        z = this->Layer0Offset * adjustedLayerThickness; // to avoid 0 layer thickness / ...
      }

      points->InsertNextPoint(x, y, z);

      for (int levelNum = 0; levelNum < this->MaximumNVertLevels; levelNum++)
      {
        if ((this->ProjectionMode != projection::SPHERICAL) &&
          (this->ProjectionMode != projection::CATALYST))
        {
          z = -(this->DepthVar[levelNum] * adjustedLayerThickness);
        }
        else if (this->ProjectionMode == projection::SPHERICAL)
        {
          if (!retval && ((x != 0.0) || (y != 0.0) || (z != 0.0)))
          {
            rholevel = rho - (adjustedLayerThickness * this->DepthVar[levelNum]);
            retval = projection::sphericalToCartesian(rholevel, phi, theta, &x, &y, &z);
          }
        }
        else if (this->ProjectionMode == projection::CATALYST)
        {
          z = -(this->DepthVar[levelNum] * (adjustedLayerThickness * 0.04));
        }
        points->InsertNextPoint(x, y, z);
      }
    }
  }
  if (abs(this->DepthVar[0] - this->Layer0Offset) < 1 && this->ShowMultilayerView)
  {
    vtkWarningMacro(<< this->FileName << ": First vertical level thickness is close to 0 ("
                    << abs(this->DepthVar[0] - this->Layer0Offset)
                    << " to be precise). It will be rendered as basically flat. Use \"3D Surface Z "
                       "offset for first Layer\" to adjust. Current value is "
                    << this->Layer0Offset << ".");
  }
  vtkDebugMacro("Leaving OutputPoints...");
}

//----------------------------------------------------------------------------
// Determine if cell is one of VTK_TRIANGLE, VTK_WEDGE, VTK_QUAD or
// VTK_HEXAHEDRON
//----------------------------------------------------------------------------
unsigned char vtkCDIReader::GetCellType()
{
  // write cell types
  switch (this->PointsPerCell)
  {
    case 3:
      return (!this->ShowMultilayerView) ? VTK_TRIANGLE : VTK_WEDGE;
    case 4:
      return (!this->ShowMultilayerView) ? VTK_QUAD : VTK_HEXAHEDRON;
    case 5:
      return (!this->ShowMultilayerView) ? VTK_POLYGON : VTK_PENTAGONAL_PRISM;
    case 6:
      return (!this->ShowMultilayerView) ? VTK_POLYGON : VTK_HEXAGONAL_PRISM;
    default:
      return (!this->ShowMultilayerView) ? VTK_POLYGON : VTK_POLYHEDRON;
  }
  return VTK_TRIANGLE;
}

//----------------------------------------------------------------------------
//  Add cells to vtk data structures
//----------------------------------------------------------------------------
void vtkCDIReader::OutputCells(bool init)
{
  vtkDebugMacro("In OutputCells...");
  vtkUnstructuredGrid* output = this->Output;

  output->GetCellData()->Initialize();
  output->GetCellData()->SetNumberOfTuples(this->MaximumCells);
  output->Allocate(this->MaximumCells, this->MaximumCells);

  vtkSmartPointer<vtkCellArray> cells;
  int pointsPerPolygon = this->PointsPerCell * (this->ShowMultilayerView ? 2 : 1);
  int cellType = this->GetCellType();

  if (init)
  {
    cells = vtkSmartPointer<vtkCellArray>::New();
    cells->Allocate(this->MaximumCells, this->MaximumCells);
    output->SetCells(cellType, cells);
    cells->SetNumberOfCells(this->MaximumCells);
  }
  else
  {
    cells = output->GetCells();
    cells->Initialize();
    cells->Allocate(this->MaximumCells, this->MaximumCells);
    cells->SetNumberOfCells(this->MaximumCells);
  }

  vtkDebugMacro("OutputCells: init: " << init << " this->MaximumCells: " << this->MaximumCells
                                      << " cellType: " << cellType
                                      << " this->MaximumNVertLevels: " << this->MaximumNVertLevels
                                      << " LayerThickness: " << this->LayerThickness
                                      << " ShowMultilayerView: " << this->ShowMultilayerView
                                      << " CurrentExtraCell: " << this->CurrentExtraCell);

  if (this->DepthVar.empty())
  {
    vtkErrorMacro(
      "File " << this->FileName << " OutputCells: this->MaximumCells: " << this->MaximumCells
              << " this->MaximumNVertLevels: " << this->MaximumNVertLevels << " LayerThickness: "
              << this->LayerThickness << " ShowMultilayerView: " << this->ShowMultilayerView);
    return; // We don't have any data anyways.
  }

  std::vector<vtkIdType> polygon(pointsPerPolygon);
  for (int j = 0; j < this->CurrentExtraCell; j++)
  {
    int* conns;
    if (this->ProjectionMode != projection::SPHERICAL)
    {
      conns = &this->ModConnections[j * this->PointsPerCell];
    }
    else
    {
      conns = &this->OrigConnections[j * this->PointsPerCell];
    }

    // check for outer boundary cell
    bool boundary = false;
    if (this->ProjectionMode == projection::CYLINDRICAL_EQUIDISTANT ||
      this->ProjectionMode == projection::CASSINI)
    {

      double borderX, borderY;
      if (this->ProjectionMode == projection::CYLINDRICAL_EQUIDISTANT)
      {
        borderX = vtkMath::Pi() + 0.5;
        borderY = (vtkMath::Pi() / 2.0) + 0.5;
      }
      else
      {
        borderX = (vtkMath::Pi() / 2.0) + 0.5;
        borderY = vtkMath::Pi() + 0.5;
      }
      for (size_t k = 0; k < this->PointsPerCell; k++)
      {
        double x = this->PointX[conns[k]];
        double y = this->PointY[conns[k]];
        if (((x > borderX) || (x < (0.0 - borderX)) || (y > borderY) || (y < (0.0 - borderY))))
        {
          boundary = true;
        }
      }
    }

    if (!this->ShowMultilayerView)
    { // singlelayer
      if (((this->GotMask) && (this->UseMask) && (this->CellMask[j] ^ this->InvertMask)) ||
        boundary)
      {
        output->InsertNextCell(VTK_EMPTY_CELL, 0, polygon.data());
      }
      else
      {
        for (int k = 0; k < this->PointsPerCell; k++)
        {
          polygon[k] = conns[k];
        }

        output->InsertNextCell(cellType, pointsPerPolygon, polygon.data());
      }
    }
    else
    { // multilayer
      for (int levelNum = 0; levelNum < this->MaximumNVertLevels; levelNum++)
      {
        int i = j * this->MaximumNVertLevels;
        if (((this->GotMask) && (this->UseMask) && (this->CellMask[i + levelNum])) || boundary)
        {
          output->InsertNextCell(VTK_EMPTY_CELL, 0, polygon.data());
        }
        else
        {
          for (int k = 0; k < this->PointsPerCell; k++)
          {
            int val = (conns[k] * (this->MaximumNVertLevels + 1)) + levelNum;
            polygon[k] = val;
          }
          for (int k = 0; k < this->PointsPerCell; k++)
          {
            int val = (conns[k] * (this->MaximumNVertLevels + 1)) + levelNum + 1;
            polygon[k + this->PointsPerCell] = val;
          }
          if (cellType == VTK_POLYHEDRON)
          {
            this->InsertPolyhedron(polygon);
          }
          else
          {
            output->InsertNextCell(cellType, pointsPerPolygon, polygon.data());
          }
        }
      }
    }
  }

  if (this->AddCoordinateVars && this->ShowClonClat)
  {
    this->ClonArray->SetName("Center Longitude (CLON)");
    this->ClonArray->SetNumberOfTuples(this->NumberLocalCells * this->MaximumNVertLevels);
    this->ClatArray->SetName("Center Latitude (CLAT)");
    this->ClatArray->SetNumberOfTuples(this->NumberLocalCells * this->MaximumNVertLevels);
    if (this->ShowMultilayerView)
    {
      this->ClonArray->SetArray(
        &this->CLon[0], this->CurrentExtraCell * this->MaximumNVertLevels, 1);
      this->ClatArray->SetArray(
        &this->CLat[0], this->CurrentExtraCell * this->MaximumNVertLevels, 1);
    }
    else
    {
      this->ClonArray->SetArray(&this->CLon[0], this->CurrentExtraCell,
        1); // 1 at the end = No freeing these. They are our vectors.
      this->ClatArray->SetArray(&this->CLat[0], this->CurrentExtraCell,
        1); // 1 at the end = No freeing these. They are our vectors.
    }
    output->GetCellData()->AddArray(this->ClonArray);
    output->GetCellData()->AddArray(this->ClatArray);
  }

  vtkDebugMacro("Leaving OutputCells...");
}

//------------------------------------------------------------------------------
void vtkCDIReader::InsertPolyhedron(std::vector<vtkIdType> polygon)
{

  std::vector<vtkIdType>::iterator it;
  it = std::unique(polygon.begin(), polygon.end());
  polygon.resize(std::distance(polygon.begin(), it));
  const int pointsPerPolygon = polygon.size();

  const int pointsPerCell = pointsPerPolygon / 2;
  const int numFaces = pointsPerCell + 2;
  std::vector<vtkIdType> facestream(7 * pointsPerCell + 2);
  // number of points of the face, then the points of the face
  // 1 + pointsPerCell values for lid / bottom
  // 1 + 4 values for each of the pointsPerCell side faces
  // bottom
  size_t next = 0;
  facestream[next++] = pointsPerCell;
  for (int k = 0; k < pointsPerCell; k++)
  {
    facestream[next++] = polygon[pointsPerCell - 1 - k];
  }
  // lid
  facestream[next++] = pointsPerCell;
  for (int k = pointsPerCell; k < pointsPerCell * 2; k++)
  {
    facestream[next++] = polygon[k];
  }
  for (int k = 0; k < pointsPerCell - 1; k++)
  {
    facestream[next++] = 4;
    facestream[next++] = polygon[k];
    facestream[next++] = polygon[k + 1];
    facestream[next++] = polygon[k + 1 + pointsPerCell];
    facestream[next++] = polygon[k + pointsPerCell];
  }
  facestream[next++] = 4;
  facestream[next++] = polygon[pointsPerCell - 1];
  facestream[next++] = polygon[0];
  facestream[next++] = polygon[pointsPerCell];
  facestream[next++] = polygon[pointsPerCell * 2 - 1];
  this->Output->InsertNextCell(
    VTK_POLYHEDRON, pointsPerPolygon, polygon.data(), numFaces, facestream.data());
};

//----------------------------------------------------------------------------
//  Load the data for a Point variable specified.
//----------------------------------------------------------------------------
int vtkCDIReader::LoadPointVarData(int variableIndex, double dTimeStep)
{
  if (!(this->PointsPerCell == 3))
  {
    return 0;
  }

  this->PointDataSelected = variableIndex;

  vtkSmartPointer<vtkDataArray> dataArray =
    this->PointVarDataArray->GetArray(this->Internals->PointVars[variableIndex].Name);

  // Allocate data array for this variable
  if (dataArray == nullptr)
  {
    if (this->DoublePrecision)
    {
      dataArray = vtkSmartPointer<vtkDoubleArray>::New();
    }
    else
    {
      dataArray = vtkSmartPointer<vtkFloatArray>::New();
    }

    vtkDebugMacro("Allocated Point var index: " << this->Internals->PointVars[variableIndex].Name);
    dataArray->SetName(this->Internals->PointVars[variableIndex].Name);
    dataArray->SetNumberOfTuples(this->MaximumPoints);
    dataArray->SetNumberOfComponents(1);

    this->PointVarDataArray->AddArray(dataArray);
  }

  int success = false;
  if (this->DoublePrecision)
  {
    vtkICONTemplateDispatch(
      VTK_DOUBLE,
      success = this->LoadPointVarDataTemplate<VTK_TT>(variableIndex, dTimeStep, dataArray););
  }
  else
  {
    vtkICONTemplateDispatch(
      VTK_FLOAT,
      success = this->LoadPointVarDataTemplate<VTK_TT>(variableIndex, dTimeStep, dataArray););
  }

  return success;
}

//----------------------------------------------------------------------------
//  Load the data for a cell variable specified.
//----------------------------------------------------------------------------
int vtkCDIReader::LoadCellVarData(int variableIndex, double dTimeStep)
{
  this->CellDataSelected = variableIndex;

  vtkSmartPointer<vtkDataArray> dataArray =
    this->CellVarDataArray->GetArray(this->Internals->CellVars[variableIndex].Name);
  // Allocate data array for this variable
  if (dataArray == nullptr)
  {
    if (this->DoublePrecision)
    {
      dataArray = vtkSmartPointer<vtkDoubleArray>::New();
    }
    else
    {
      dataArray = vtkSmartPointer<vtkFloatArray>::New();
    }

    vtkDebugMacro("Allocated cell var index: " << this->Internals->CellVars[variableIndex].Name);
    dataArray->SetName(this->Internals->CellVars[variableIndex].Name);
    dataArray->SetNumberOfTuples(this->MaximumCells);
    dataArray->SetNumberOfComponents(1);

    this->CellVarDataArray->AddArray(dataArray);
  }

  int success = false;
  if (this->DoublePrecision)
  {
    vtkICONTemplateDispatch(
      VTK_DOUBLE,
      success = this->LoadCellVarDataTemplate<VTK_TT>(variableIndex, dTimeStep, dataArray););
  }
  else
  {
    vtkICONTemplateDispatch(
      VTK_FLOAT,
      success = this->LoadCellVarDataTemplate<VTK_TT>(variableIndex, dTimeStep, dataArray););
  }

  return success;
}

//----------------------------------------------------------------------------
//  Load the data for a cell variable specified.
//----------------------------------------------------------------------------
template <typename ValueType>
int vtkCDIReader::LoadCellVarDataTemplate(
  int variableIndex, double dTimeStep, vtkDataArray* dataArray)
{
  vtkDebugMacro("In vtkCDIReader::LoadCellVarData");
  ValueType* dataBlock = static_cast<ValueType*>(dataArray->GetVoidPointer(0));
  cdi_tools::CDIVar* cdiVar = &(this->Internals->CellVars[variableIndex]);
  int varType = cdiVar->Type;

  int timestep = this->GetTimeIndex(dTimeStep);
  vtkDebugMacro("Time: " << timestep);
  vtkDebugMacro("Dimensions: " << varType);

  if (varType == 3) // 3D arrays
  {
    if (!this->ShowMultilayerView)
    {
      cdi_set_cur(cdiVar, timestep, this->VerticalLevelSelected);
      cdi_tools::cdi_get_part<ValueType>(
        cdiVar, this->BeginCell, this->NumberLocalCells, dataBlock, 1, this->Grib);

      // put out data for extra cells
      for (int j = this->NumberLocalCells; j < this->CurrentExtraCell; j++)
      {
        int k = this->CellMap[j - this->NumberLocalCells];
        dataBlock[j] = dataBlock[k];
      }
    }
    else
    {
      ValueType* dataTmp = new ValueType[this->MaximumCells];
      cdi_set_cur(cdiVar, timestep, 0);
      cdi_tools::cdi_get_part<ValueType>(cdiVar, this->BeginCell, this->NumberLocalCells, dataTmp,
        this->MaximumNVertLevels, this->Grib);

      // readjust the data
      for (int j = 0; j < this->NumberLocalCells; j++)
      {
        for (int levelNum = 0; levelNum < this->MaximumNVertLevels; levelNum++)
        {
          int i = j * this->MaximumNVertLevels;
          dataBlock[i + levelNum] = dataTmp[j + (levelNum * this->NumberLocalCells)];
        }
      }

      // put out data for extra cells
      for (int j = this->NumberLocalCells; j < this->CurrentExtraCell; j++)
      {
        for (int levelNum = 0; levelNum < this->MaximumNVertLevels; levelNum++)
        {
          int l = j * this->MaximumNVertLevels;
          int k = this->CellMap[j - this->NumberLocalCells];
          dataBlock[l + levelNum] = dataTmp[k + (levelNum * this->NumberLocalCells)];
        }
      }

      delete[] dataTmp;
    }
    vtkDebugMacro("Got data for cell var: " << this->Internals->CellVars[variableIndex].Name);
  }
  else // 2D arrays
  {
    if (!this->ShowMultilayerView)
    {
      cdi_set_cur(cdiVar, timestep, 0);
      cdi_tools::cdi_get_part<ValueType>(
        cdiVar, this->BeginCell, this->NumberLocalCells, dataBlock, 1, this->Grib);

      // put out data for extra cells
      for (int j = this->NumberLocalCells; j < this->CurrentExtraCell; j++)
      {
        int k = this->CellMap[j - this->NumberLocalCells];
        dataBlock[j] = dataBlock[k];
      }
    }
    else
    {
      ValueType* dataTmp = new ValueType[this->NumberLocalCells];
      cdi_set_cur(cdiVar, timestep, 0);
      cdi_tools::cdi_get_part<ValueType>(
        cdiVar, this->BeginCell, this->NumberLocalCells, dataTmp, 1, this->Grib);

      for (int j = 0; j < +this->NumberLocalCells; j++)
      {
        for (int levelNum = 0; levelNum < this->MaximumNVertLevels; levelNum++)
        {
          int i = j * this->MaximumNVertLevels;
          dataBlock[i + levelNum] = dataTmp[j];
        }
      }

      // put out data for extra cells
      for (int j = this->NumberLocalCells; j < this->CurrentExtraCell; j++)
      {
        for (int levelNum = 0; levelNum < this->MaximumNVertLevels; levelNum++)
        {
          int l = j * this->MaximumNVertLevels;
          int k = this->CellMap[j - this->NumberLocalCells];
          dataBlock[l + levelNum] = dataTmp[k];
        }
      }

      delete[] dataTmp;
    }

    vtkDebugMacro("Got data for cell var: " << this->Internals->CellVars[variableIndex].Name);
  }

  vtkDebugMacro("Stored data for cell var: " << this->Internals->CellVars[variableIndex].Name);

  this->ReplaceFillWithNan(cdiVar->VarID, dataArray);
  return 1;
}

//------------------------------------------------------------------------------
int vtkCDIReader::ReplaceFillWithNan(const int varID, vtkDataArray* dataArray)
{
  double miss = vlistInqVarMissval(this->Internals->DataFile.getVListID(), varID);

  // NaN only available with float and double.
  if (dataArray->GetDataType() == VTK_FLOAT)
  {

    float fillValue = miss;
    std::replace(reinterpret_cast<float*>(dataArray->GetVoidPointer(0)),
      reinterpret_cast<float*>(dataArray->GetVoidPointer(dataArray->GetNumberOfTuples())),
      fillValue, static_cast<float>(vtkMath::Nan()));
  }
  else if (dataArray->GetDataType() == VTK_DOUBLE)
  {
    double fillValue = miss;
    std::replace(reinterpret_cast<double*>(dataArray->GetVoidPointer(0)),
      reinterpret_cast<double*>(dataArray->GetVoidPointer(dataArray->GetNumberOfTuples())),
      fillValue, vtkMath::Nan());
  }
  else
  {
    vtkWarningMacro(<< "No NaN available for data of type " << dataArray->GetDataType());
  }

  return 1;
}

//----------------------------------------------------------------------------
//  Load the data for a Point variable specified.
//----------------------------------------------------------------------------
template <typename ValueType>
int vtkCDIReader::LoadPointVarDataTemplate(
  int variableIndex, double dTimeStep, vtkDataArray* dataArray)
{
  vtkDebugMacro("In vtkICONReader::LoadPointVarData");
  cdi_tools::CDIVar* cdiVar = &this->Internals->PointVars[variableIndex];
  int varType = cdiVar->Type;

  vtkDebugMacro("getting pointer in vtkICONReader::LoadPointVarData");
  ValueType* dataBlock = static_cast<ValueType*>(dataArray->GetVoidPointer(0));
  ValueType* dataTmp;
  if (this->ShowMultilayerView)
  {
    dataTmp = new ValueType[this->MaximumPoints];
  }
  else
  {
    dataTmp = new ValueType[this->NumberLocalPoints];
  }

  int timestep = this->GetTimeIndex(dTimeStep);
  vtkDebugMacro("Time: " << timestep);
  vtkDebugMacro("dTimeStep requested: " << dTimeStep);

  if (this->Piece < 1)
  {
    // 3D arrays ...
    vtkDebugMacro("Dimensions: " << varType);
    if (varType == 3)
    {
      if (!this->ShowMultilayerView)
      {
        cdi_set_cur(cdiVar, timestep, this->VerticalLevelSelected);
        cdi_tools::cdi_get_part<ValueType>(
          cdiVar, this->BeginPoint, this->NumberLocalPoints, dataBlock, 1, this->Grib);
        dataBlock[0] = dataBlock[1];

        // put out data for extra points
        for (int j = this->NumberLocalPoints; j < this->CurrentExtraPoint; j++)
        {
          int k = this->PointMap[j - this->NumberLocalPoints];
          dataBlock[j] = dataBlock[k];
        }
      }
      else
      {
        cdi_set_cur(cdiVar, timestep, 0);
        cdi_tools::cdi_get_part<ValueType>(cdiVar, this->BeginPoint, this->NumberLocalPoints,
          dataTmp, this->MaximumNVertLevels, this->Grib);
        dataTmp[0] = dataTmp[1];

        // put out data for extra points
        for (int j = this->NumberLocalPoints; j < this->CurrentExtraPoint; j++)
        {
          for (int levelNum = 0; levelNum < this->MaximumNVertLevels; levelNum++)
          {
            int l = j * this->MaximumNVertLevels;
            int k = this->PointMap[j - this->NumberLocalPoints];
            dataBlock[l + levelNum] = dataTmp[k + (levelNum * this->NumberLocalPoints)];
          }
        }
      }
    }
    // 2D arrays ...
    else if (varType == 2)
    {
      if (!this->ShowMultilayerView)
      {
        cdi_set_cur(cdiVar, timestep, 0);
        cdi_tools::cdi_get_part<ValueType>(
          cdiVar, this->BeginPoint, this->NumberLocalPoints, dataBlock, 1, this->Grib);
        dataBlock[0] = dataBlock[1];
      }
      else
      {
        cdi_set_cur(cdiVar, timestep, 0);
        cdi_tools::cdi_get_part<ValueType>(
          cdiVar, this->BeginPoint, this->NumberLocalPoints, dataTmp, 1, this->Grib);
        dataTmp[0] = dataTmp[1];
      }
    }
    vtkDebugMacro("got Point data in vtkICONReader::LoadPointVarDataSP");

    if (this->ShowMultilayerView)
    {
      // put in some dummy points
      for (int levelNum = 0; levelNum < this->MaximumNVertLevels; levelNum++)
      {
        dataBlock[levelNum] = dataTmp[this->MaximumNVertLevels + levelNum];
      }

      // write highest level dummy Point (duplicate of last level)
      dataBlock[this->MaximumNVertLevels] =
        dataTmp[this->MaximumNVertLevels + this->MaximumNVertLevels - 1];
      vtkDebugMacro("Wrote dummy vtkICONReader::LoadPointVarDataSP");

      // readjust the data
      for (int j = 0; j < this->NumberLocalPoints; j++)
      {
        int i = j * (this->MaximumNVertLevels + 1);
        // write data for one Point -- lowest level to highest
        for (int levelNum = 0; levelNum < this->MaximumNVertLevels; levelNum++)
        {
          dataBlock[i++] = dataTmp[j + (levelNum * this->NumberLocalPoints)];
        }

        // layer below, which is repeated ...
        dataBlock[i++] = dataTmp[j + ((this->MaximumNVertLevels - 1) * this->NumberLocalPoints)];
      }
    }
  }
  else
  {
    int length = (this->NumberAllPoints / this->NumPieces);
    int start = (length * this->Piece);
    ValueType* dataTmp2 = new ValueType[length];

    // 3D arrays ...
    vtkDebugMacro("Dimensions: " << varType);
    if (varType == 3)
    {
      if (!this->ShowMultilayerView)
      {
        cdi_set_cur(cdiVar, timestep, this->VerticalLevelSelected);
        cdi_tools::cdi_get_part<ValueType>(cdiVar, start, length, dataTmp2, 1, this->Grib);
        dataTmp2[0] = dataTmp2[1];

        // readjust the data
        size_t size = this->NumberLocalCells * this->PointsPerCell;
        for (size_t j = 0; j < size; j++)
        {
          int pos = this->VertexIds[j];
          int pos_conn = this->OrigConnections[j];
          if ((pos > start) && (pos < (start + length + 1)))
          {
            dataBlock[pos_conn] = dataTmp2[pos - start];
          }
          else
          {
            dataBlock[pos_conn] = 0.0;
          }
        }
      }
      else
      {
        cdi_set_cur(cdiVar, timestep, 0);
        cdi_tools::cdi_get_part<ValueType>(
          cdiVar, start, length, dataTmp, this->MaximumNVertLevels, this->Grib);
        dataTmp[0] = dataTmp[1];
      }
    }
    // 2D arrays ...
    else if (varType == 2)
    {
      if (!this->ShowMultilayerView)
      {
        cdi_set_cur(cdiVar, timestep, 0);
        cdi_tools::cdi_get_part<ValueType>(cdiVar, start, length, dataTmp2, 1, this->Grib);
        dataTmp2[0] = dataTmp2[1];

        // readjust the data
        size_t size = this->NumberLocalCells * this->PointsPerCell;
        for (size_t j = 0; j < size; j++)
        {
          int pos = this->VertexIds[j];
          int pos_conn = this->OrigConnections[j];
          if ((pos > start) && (pos < (start + length + 1)))
          {
            dataBlock[pos_conn] = dataTmp2[pos - start];
          }
          else
          {
            dataBlock[pos_conn] = 0.0;
          }
        }
      }
      else
      {
        cdi_set_cur(cdiVar, timestep, 0);
        cdi_tools::cdi_get_part<ValueType>(cdiVar, start, length, dataTmp, 1, this->Grib);
        dataTmp[0] = dataTmp[1];
      }
    }
    delete[] dataTmp2;
    vtkDebugMacro("got Point data in vtkICONReader::LoadPointVarDataSP");
  }

  vtkDebugMacro("this->NumberOfPoints: " << this->NumberOfPoints << " this->NumberLocalPoints: "
                                         << this->NumberLocalPoints);
  delete[] dataTmp;

  return 1;
}

//----------------------------------------------------------------------------
//  Load the data for a domain variable specified
//----------------------------------------------------------------------------
int vtkCDIReader::LoadDomainVarData(int variableIndex)
{
  // This is not very well implemented, also due to the organization of
  // the data available. Needs to be improved together with the modellers.
  vtkDebugMacro("In vtkCDIReader::LoadDomainVarData");
  std::string variable = this->Internals->DomainVars[variableIndex];

  // Allocate data array for this variable
  if (!this->DomainVarDataArray->HasArray(variable.c_str()))
  {
    vtkNew<vtkDoubleArray> dataArray;
    vtkDebugMacro("Allocated domain var index: " << variable.c_str());
    dataArray->SetName(variable.c_str());
    dataArray->SetNumberOfTuples(this->NumberOfDomains);
    // 6 components
    dataArray->SetNumberOfComponents(1);
    this->DomainVarDataArray->AddArray(dataArray);
  }

  for (int i = 0; i < this->NumberOfDomains; i++)
  {
    std::string filename;
    if (i < 10)
    {
      filename = this->PerformanceDataFile + "000" + std::to_string(i);
    }
    else if (i < 100)
    {
      filename = this->PerformanceDataFile + "00" + std::to_string(i);
    }
    else if (i < 1000)
    {
      filename = this->PerformanceDataFile + "0" + std::to_string(i);
    }
    else
    {
      filename = this->PerformanceDataFile + std::to_string(i);
    }

    std::vector<std::string> wordVec;
    vtksys::ifstream file(filename.c_str());
    std::string str, word;
    double temp[1];

    getline(file, str);
    std::stringstream ss(str);
    while (ss.good())
    {
      ss >> word;

      word.erase(word.begin(),
        std::find_if(word.begin(), word.end(), [](int c) { return !std::ispunct(c); }));
      word.erase(
        std::find_if(word.rbegin(), word.rend(), [](int c) { return !std::ispunct(c); }).base(),
        word.end());

      wordVec.push_back(word);
    }
    //  0    1      	2     		3      	4       	5      	6 7		  8
    //  th  L 	name   	#calls 	t_min 	t_ave	t_max 	t_total	   t_total2
    // 00   L		physics   251    	0.4222s  0.9178s  10.52s    03m50s     230.21174
    // for (int l=0; l<6 ; l++)
    //  temp[l] = atof(wordVec.at(2+l).c_str());

    if (wordVec.at(1) != "L")
    {
      temp[0] = atof(wordVec.at(7).c_str());
    }
    else
    {
      temp[0] = atof(wordVec.at(8).c_str());
    }

    // for now, we just use t_average
    this->DomainVarDataArray->GetArray(variableIndex)->InsertTuple(i, temp);
  }

  vtkDebugMacro("Out vtkCDIReader::LoadDomainVarData");
  return 1;
}

//----------------------------------------------------------------------------
//  Specify the netCDF dimension names
//-----------------------------------------------------------------------------
int vtkCDIReader::FillGridDimensions()
{
  this->Internals->DimensionSets.clear();

  int ngrids = vlistNgrids(this->Internals->DataFile.getVListID());
  int nzaxis = vlistNzaxis(this->Internals->DataFile.getVListID());
  int nvars = vlistNvars(this->Internals->DataFile.getVListID());
  char nameGridX[CDI_MAX_NAME];
  char nameGridY[CDI_MAX_NAME];
  char nameLev[CDI_MAX_NAME];

  std::set<std::string> hits;

  for (int k = 0; k < nvars; k++)
  {
    int i = vlistInqVarGrid(this->Internals->DataFile.getVListID(), k);
    int j = vlistInqVarZaxis(this->Internals->DataFile.getVListID(), k);
    hits.insert(std::to_string(i) + "x" + std::to_string(j));
    // IDs are not 0 to n-1 but can be 30-ish for a file with 3 grids.
    // they map to the gridID_l and zaxisID_l values below.
    // Thus we need to a map to catch rather unpredictable values.
  }
  size_t counter = 0;
  for (int i = 0; i < ngrids; ++i)
  {
    for (int j = 0; j < nzaxis; ++j)
    {
      std::string dimEncoding("(");
      int gridID_l = vlistGrid(this->Internals->DataFile.getVListID(), i);
      gridInqXname(gridID_l, nameGridX);
      gridInqYname(gridID_l, nameGridY);
      dimEncoding += nameGridX;
      dimEncoding += ", ";
      dimEncoding += nameGridY;
      dimEncoding += ", ";
      int zaxisID_l = vlistZaxis(this->Internals->DataFile.getVListID(), j);
      zaxisInqName(zaxisID_l, nameLev);
      dimEncoding += nameLev;
      dimEncoding += ")";

      if (hits.count(std::to_string(gridID_l) + "x" + std::to_string(zaxisID_l)) == 0)
      {
        vtkDebugMacro("vtkCDIReader::FillGridDimensions: i, j, dimEncoding: "
          << i << '\t' << j << "\t" << gridID_l << '\t' << zaxisID_l << "\t" << dimEncoding
          << " - has no hits.\n");
        continue; // skip empty grid combinations
      }
      vtkDebugMacro("vtkCDIReader::FillGridDimensions: i, j, GridID, ZAxisID, dimEncoding: "
        << i << '\t' << j << "\t" << gridID_l << '\t' << zaxisID_l << "\t" << dimEncoding
        << " - has hits.\n");

      Dimset ds{ .DimsetID = counter,
        .GridID = -1,
        .ZAxisID = zaxisID_l,
        .GridSize = static_cast<size_t>(gridInqSize(gridID_l)),
        .NLevel = zaxisInqSize(zaxisID_l),
        .label = dimEncoding };
      this->Internals->DimensionSets[dimEncoding] = ds;
      counter++;
    }
  }
  this->AllDimensions->SetNumberOfValues(counter);
  this->VariableDimensions->SetNumberOfValues(counter);

  int i = 0;
  for (const auto& label_diset_tuple : this->Internals->DimensionSets)
  {
    this->AllDimensions->SetValue(i, label_diset_tuple.first);
    this->VariableDimensions->SetValue(i, label_diset_tuple.first.c_str());
    ++i;
  }

  return 1;
}

//----------------------------------------------------------------------------
//  Set dimensions and rebuild geometry
//-----------------------------------------------------------------------------
void vtkCDIReader::SetDimensions(const char* dimensions)
{
  vtkDebugMacro("In SetDimensions");
  this->DimensionSelection = dimensions;

  this->PointDataArraySelection->RemoveAllArrays();
  this->CellDataArraySelection->RemoveAllArrays();
  this->PointDataArraySelection->Modified();
  this->CellDataArraySelection->Modified();

  if (this->DomainDataArraySelection)
  {
    this->DomainDataArraySelection->RemoveAllArrays();
    this->DomainDataArraySelection->Modified();
  }

  this->ReconstructNew = true;
  this->DestroyData();
  this->RegenerateVariables();
  if (this->GridReconstructed)
  {
    this->RegenerateGeometry();
  }
  vtkDebugMacro("Out SetDimensions");
}

//----------------------------------------------------------------------------
//  Return all variable names
//-----------------------------------------------------------------------------
vtkStringArray* vtkCDIReader::GetAllVariableArrayNames()
{
  int numArrays = this->GetNumberOfVariableArrays();
  this->AllVariableArrayNames->SetNumberOfValues(numArrays);
  for (int arrayIdx = 0; arrayIdx < numArrays; arrayIdx++)
  {
    const char* arrayName = this->GetVariableArrayName(arrayIdx);
    this->AllVariableArrayNames->SetValue(arrayIdx, arrayName);
  }
  return this->AllVariableArrayNames;
}

//----------------------------------------------------------------------------
// Set status of named Point variable selection.
//----------------------------------------------------------------------------
void vtkCDIReader::SetPointArrayStatus(const char* name, int status)
{
  if (status)
  {
    this->PointDataArraySelection->EnableArray(name);
  }
  else
  {
    this->PointDataArraySelection->DisableArray(name);
  }
}

//----------------------------------------------------------------------------
// Set status of named cell variable selection.
//----------------------------------------------------------------------------
void vtkCDIReader::SetCellArrayStatus(const char* name, int status)
{
  if (status)
  {
    this->CellDataArraySelection->EnableArray(name);
  }
  else
  {
    this->CellDataArraySelection->DisableArray(name);
  }
}

//----------------------------------------------------------------------------
// Set status of named domain variable selection.
//----------------------------------------------------------------------------
void vtkCDIReader::SetDomainArrayStatus(const char* name, int status)
{
  if (status)
  {
    this->DomainDataArraySelection->EnableArray(name);
  }
  else
  {
    this->DomainDataArraySelection->DisableArray(name);
  }
}

//----------------------------------------------------------------------------
// Set status of named domain variable selection.
//----------------------------------------------------------------------------
void vtkCDIReader::SetMaskingVariable(const char* name)
{
  vtkDebugMacro("Setting MaskingVariable to " << name);

  this->MaskingVarname = name;

  this->Modified();

  if (!this->InfoRequested || !this->DataRequested)
  {
    return;
  }
  this->DestroyData();
  this->RegenerateGeometry();
}

//----------------------------------------------------------------------------
// Toggle the Use of the custom mask value.
//----------------------------------------------------------------------------
void vtkCDIReader::SetUseCustomMaskValue(bool val)
{
  if (val == this->UseCustomMaskValue)
  {
    return;
  }

  this->UseCustomMaskValue = val;

  this->Modified();

  if (!this->InfoRequested || !this->DataRequested || !this->UseCustomMaskValue)
  {
    return;
  }
  this->DestroyData();
  this->RegenerateGeometry();
}

//----------------------------------------------------------------------------
// Set custom mask value.
//----------------------------------------------------------------------------
void vtkCDIReader::SetCustomMaskValue(double val)
{
  if (val == this->CustomMaskValue)
  {
    return;
  }

  this->CustomMaskValue = val;

  this->Modified();

  if (!this->InfoRequested || !this->DataRequested || !this->UseCustomMaskValue)
  {
    return;
  }
  this->DestroyData();
  this->RegenerateGeometry();
}

//----------------------------------------------------------------------------
// Get name of indexed Point variable
//----------------------------------------------------------------------------
const char* vtkCDIReader::GetPointArrayName(int index)
{
  return this->Internals->PointVars[index].Name;
}

//----------------------------------------------------------------------------
// Get name of indexed cell variable
//----------------------------------------------------------------------------
const char* vtkCDIReader::GetCellArrayName(int index)
{
  return this->Internals->CellVars[index].Name;
}

//----------------------------------------------------------------------------
// Get name of indexed domain variable
//----------------------------------------------------------------------------
const char* vtkCDIReader::GetDomainArrayName(int index)
{
  return this->Internals->DomainVars[index].c_str();
}

//----------------------------------------------------------------------------
// Load a new file.
//----------------------------------------------------------------------------
void vtkCDIReader::SetFileName(const char* val)
{
  if (this->FileName.empty() || val == nullptr || strcmp(this->FileName.c_str(), val) != 0)
  {
    this->Internals->DataFile.setVoid();
    this->Modified();
    if (val == nullptr)
    {
      return;
    }
    this->FileName = val;
    vtkDebugMacro("SetFileName to " << this->FileName);

    this->DestroyData();
    this->RegenerateVariables();
  }
}

//----------------------------------------------------------------------------
//  Set vertical level to be viewed.
//----------------------------------------------------------------------------
void vtkCDIReader::SetVerticalLevel(int level)
{
  if (this->VerticalLevelSelected != level)
  {
    if (level < 0)
    {
      vtkErrorMacro("Requested inexistent vertical level: "
        << level << ".\nThe level must be the in range [ 0 ; " << this->MaximumNVertLevels - 1
        << " ].");
      return;
    }

    if (level > this->MaximumNVertLevels - 1)
    {
      vtkWarningMacro("Requested inexistent vertical level: "
        << level << ".\nThe level must be the in range [ 0 ; " << this->MaximumNVertLevels - 1
        << " ]. \nTriying with 0.");
      level = 0;
    }
    this->VerticalLevelSelected = level;
    this->Modified();
    vtkDebugMacro("Set VerticalLevelSelected to: " << level);
  }
  vtkDebugMacro("InfoRequested?: " << this->InfoRequested);

  if (!this->InfoRequested || !this->DataRequested)
  {
    return;
  }

  for (int var = 0; var < this->NumberOfPointVars; var++)
  {
    if (this->PointDataArraySelection->GetArraySetting(var))
    {
      vtkDebugMacro("Loading Point Variable: " << this->Internals->PointVars[var].Name);
      this->LoadPointVarData(var, this->DTime);
    }
  }

  for (int var = 0; var < this->NumberOfCellVars; var++)
  {
    if (this->CellDataArraySelection->GetArraySetting(var))
    {
      vtkDebugMacro("Loading Cell Variable: " << this->Internals->CellVars[var].Name);
      this->LoadCellVarData(var, this->DTime);
    }
  }

  this->PointDataArraySelection->Modified();
  this->CellDataArraySelection->Modified();
}

//----------------------------------------------------------------------------
//  Set layer thickness for multilayer view.
//----------------------------------------------------------------------------
void vtkCDIReader::SetLayerThickness(int val)
{
  if (this->LayerThickness != val)
  {
    this->LayerThickness = val;
    this->Modified();
    vtkDebugMacro("SetLayerThickness: LayerThickness set to " << this->LayerThickness);

    if (this->ShowMultilayerView)
    {
      if (!this->InfoRequested || !this->DataRequested)
      {
        return;
      }
      this->DestroyData();
      this->RegenerateGeometry();
    }
  }
}

//----------------------------------------------------------------------------
//  Set layer 0 offset for multilayer view.
//----------------------------------------------------------------------------
void vtkCDIReader::SetLayer0Offset(double val)
{
  if (this->Layer0Offset != val)
  {
    this->Layer0Offset = val;
    this->Modified();
    vtkDebugMacro("SetLayer0Offset: Layer0Offset set to " << this->Layer0Offset);

    if (this->ShowMultilayerView)
    {
      if (!this->InfoRequested || !this->DataRequested)
      {
        return;
      }
      this->DestroyData();
      this->RegenerateGeometry();
    }
  }
}

//----------------------------------------------------------------------------
// Set the projection mode.
//----------------------------------------------------------------------------
void vtkCDIReader::SetProjection(int val)
{
  if (this->ProjectionMode != val)
  {
    if (!projection::isproj(val))
    {
      vtkErrorMacro(
        "SetProjection: Can't set ProjectionMode to '" << val << "'. Not a valid projection.");
      return;
    }
    this->ProjectionMode = projection::Projection(val);
    this->Modified();
    vtkDebugMacro("SetProjection: ProjectionMode to " << this->ProjectionMode);
    this->ReconstructNew = true;

    if (!this->InfoRequested || !this->DataRequested)
    {
      return;
    }

    this->DestroyData();
    this->RegenerateGeometry();
  }
}

//----------------------------------------------------------------------------
// Set double/float projection.
//----------------------------------------------------------------------------
void vtkCDIReader::SetDoublePrecision(bool val)
{
  if (this->DoublePrecision != val)
  {
    this->DoublePrecision = val;
    this->Modified();
    vtkDebugMacro("DoublePrecision to " << this->DoublePrecision);
    this->ReconstructNew = true;

    if (!this->InfoRequested || !this->DataRequested)
    {
      return;
    }

    this->DestroyData();
    this->RegenerateGeometry();
  }
}

//----------------------------------------------------------------------------
// Set wrapping.
//----------------------------------------------------------------------------
void vtkCDIReader::SetWrapping(bool val)
{
  if (this->WrapOn != val)
  {
    this->WrapOn = val;
    this->Modified();
    vtkDebugMacro("Wrapping set to " << this->WrapOn);
    this->ReconstructNew = true;

    if (!this->InfoRequested || !this->DataRequested)
    {
      return;
    }

    this->DestroyData();
    this->RegenerateGeometry();
  }
}

//----------------------------------------------------------------------------
// Invert the z-axis of the visualization.
//----------------------------------------------------------------------------
void vtkCDIReader::SetInvertZAxis(bool val)
{
  if (this->InvertZAxis != val)
  {
    this->InvertZAxis = val;
    this->Modified();
    vtkDebugMacro("InvertZAxis to " << this->InvertZAxis);

    if (!this->InfoRequested || !this->DataRequested)
    {
      return;
    }

    this->DestroyData();
    this->RegenerateGeometry();
  }
}

//----------------------------------------------------------------------------
// Set visualization with topography/bathymetrie.
//----------------------------------------------------------------------------
void vtkCDIReader::SetUseMask(bool val)
{
  if (this->UseMask != val)
  {
    this->UseMask = val;
    this->Modified();
    vtkDebugMacro("Set UseMask to " << this->UseMask);

    if (!this->InfoRequested || !this->DataRequested)
    {
      return;
    }

    this->DestroyData();
    this->RegenerateGeometry();
  }
}

//----------------------------------------------------------------------------
// Add Clon and Clat to output
//----------------------------------------------------------------------------
void vtkCDIReader::SetShowClonClat(bool val)
{
  if (this->ShowClonClat != val)
  {
    this->ShowClonClat = val;
    this->Modified();
    vtkDebugMacro("Set ShowClonClat to " << this->ShowClonClat);

    if (!this->InfoRequested || !this->DataRequested)
    {
      return;
    }

    this->DestroyData();
    this->RegenerateGeometry();
  }
}

//----------------------------------------------------------------------------
// Set visualization with inverted topography/bathymetrie.
//----------------------------------------------------------------------------
void vtkCDIReader::SetInvertMask(bool val)
{
  if (val == this->InvertMask)
  {
    return;
  }
  this->InvertMask = val;
  this->Modified();

  vtkDebugMacro("Set InvertMask to " << this->InvertMask);

  if (!this->InfoRequested || !this->DataRequested)
  {
    return;
  }

  this->DestroyData();
  this->RegenerateGeometry();
}

//------------------------------------------------------------------------------
void vtkCDIReader::SetSkipGrid(bool val)
{
  this->SkipGrid = val;
}

//----------------------------------------------------------------------------
//  Set view to be multilayered view.
//----------------------------------------------------------------------------
void vtkCDIReader::SetShowMultilayerView(bool val)
{
  if (this->ShowMultilayerView != val)
  {
    this->ShowMultilayerView = val;
    this->Modified();
    vtkDebugMacro("ShowMultilayerView to " << this->ShowMultilayerView);

    if (!this->InfoRequested || !this->DataRequested)
    {
      return;
    }

    this->DestroyData();
    this->RegenerateGeometry();
  }
}

//----------------------------------------------------------------------------
//  Print self.
//----------------------------------------------------------------------------
void vtkCDIReader::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);
  os << indent << "FileName: " << (this->FileName.c_str() ? this->FileName.c_str() : "nullptr")
     << "\n";
  os << indent << "VariableDimensions: ";
  if (this->VariableDimensions)
  {
    this->VariableDimensions->PrintSelf(os, indent.GetNextIndent());
  }
  else
  {
    os << "(nullptr)"
       << "\n";
  }
  os << indent << "AllDimensions: ";
  if (this->AllDimensions)
  {
    this->AllDimensions->PrintSelf(os, indent.GetNextIndent());
  }
  else
  {
    os << "(nullptr)"
       << "\n";
  }
  os << indent << "NumberOfPointVars: " << this->NumberOfPointVars << "\n";
  os << indent << "NumberOfCellVars: " << this->NumberOfCellVars << "\n";
  os << indent << "NumberOfDomainVars: " << this->NumberOfDomainVars << "\n";
  os << indent << "MaximumPoints: " << this->MaximumPoints << "\n";
  os << indent << "MaximumCells: " << this->MaximumCells << "\n";
  os << indent << "Projection: " << this->ProjectionMode << endl;
  os << indent << "DoublePrecision: " << (this->DoublePrecision ? "ON" : "OFF") << endl;
  os << indent << "Wrapping: " << (this->WrapOn ? "ON" : "OFF") << endl;
  os << indent << "ShowClonClat: " << (this->ShowClonClat ? "ON" : "OFF") << endl;
  os << indent << "ShowMultilayerView: " << (this->ShowMultilayerView ? "ON" : "OFF") << endl;
  os << indent << "InvertZ: " << (this->InvertZAxis ? "ON" : "OFF") << endl;
  os << indent << "UseMask: " << (this->UseMask ? "ON" : "OFF") << endl;
  os << indent << "CustomMaskValue: " << this->CustomMaskValue << endl;
  os << indent << "SkipGrid: " << (this->SkipGrid ? "ON" : "OFF") << endl;
  os << indent << "InvertMask: " << (this->InvertMask ? "ON" : "OFF") << endl;
  os << indent << "VerticalLevel: " << this->VerticalLevelSelected << "\n";
  os << indent << "VerticalLevelRange: " << this->VerticalLevelRange[0] << ","
     << this->VerticalLevelRange[1] << endl;
  os << indent << "LayerThicknessRange: " << this->LayerThicknessRange[0] << ","
     << this->LayerThicknessRange[1] << endl;
  os << indent << "Layer0OffsetRange: " << this->Layer0OffsetRange[0] << ","
     << this->Layer0OffsetRange[1] << endl;
}
