diff --git a/IO/Geometry/vtkOpenFOAMReader.cxx b/IO/Geometry/vtkOpenFOAMReader.cxx index 978c6b172554354aaafcb7d820924894a09240d7..d2786cc778c8a1795d1dede3fbc6687413771658 100644 --- a/IO/Geometry/vtkOpenFOAMReader.cxx +++ b/IO/Geometry/vtkOpenFOAMReader.cxx @@ -1000,17 +1000,24 @@ private: class vtkOpenFOAMReaderPrivate : public vtkObject { public: + // Use sparingly + friend class vtkOpenFOAMReader; + static vtkOpenFOAMReaderPrivate* New(); vtkTypeMacro(vtkOpenFOAMReaderPrivate, vtkObject); vtkGetMacro(TimeStep, int); vtkSetMacro(TimeStep, int); + double GetTimeValue() const; void SetTimeValue(double requestedTime); vtkStringArray* GetTimeNames() { return this->TimeNames; } vtkDoubleArray* GetTimeValues() { return this->TimeValues; } + // Print some time information (names, current time-step) + void PrintTimes(std::ostream& os, vtkIndent indent, bool full = false) const; + bool HasPolyMesh() const noexcept { return !this->PolyMeshTimeIndexFaces.empty(); } const std::string& GetRegionName() const noexcept { return this->RegionName; } @@ -1025,6 +1032,11 @@ public: bool MakeInformationVector(const std::string& casePath, const std::string& controlDictPath, const std::string& procName, vtkOpenFOAMReader* parent, bool requirePolyMesh = true); + // Use given time instances information and create cache for mesh times + bool MakeInformationVector(const std::string& casePath, const std::string& procName, + vtkOpenFOAMReader* parent, vtkStringArray* timeNames, vtkDoubleArray* timeValues, + bool requirePolyMesh = true); + // Copy time instances information and create cache for mesh times void SetupInformation(const std::string& casePath, const std::string& regionName, const std::string& procName, vtkOpenFOAMReaderPrivate* master, bool requirePolyMesh = true); @@ -5518,27 +5530,78 @@ void vtkOpenFOAMReaderPrivate::ClearMeshes() this->ClearAreaMeshes(); } +//------------------------------------------------------------------------------ +// Time handling + +double vtkOpenFOAMReaderPrivate::GetTimeValue() const +{ + const vtkIdType nTimes = this->TimeValues->GetNumberOfTuples(); + + if (this->TimeStep < 0 || this->TimeStep >= nTimes) + { + return 0; + } + + return TimeValues->GetValue(this->TimeStep); +} + void vtkOpenFOAMReaderPrivate::SetTimeValue(double requestedTime) { - const vtkIdType nTimeValues = this->TimeValues->GetNumberOfTuples(); + const vtkIdType nTimes = this->TimeValues->GetNumberOfTuples(); - if (nTimeValues > 0) + if (nTimes) { - int minTimeI = 0; - double minTimeDiff = fabs(this->TimeValues->GetValue(0) - requestedTime); - for (int timeI = 1; timeI < nTimeValues; timeI++) + int nearestIndex = 0; + double deltaT = fabs(this->TimeValues->GetValue(0) - requestedTime); + + for (vtkIdType timei = 1; timei < nTimes; ++timei) { - const double timeDiff(fabs(this->TimeValues->GetValue(timeI) - requestedTime)); - if (timeDiff < minTimeDiff) + const double diff = fabs(this->TimeValues->GetValue(timei) - requestedTime); + if (diff < deltaT) { - minTimeI = timeI; - minTimeDiff = timeDiff; + deltaT = diff; + nearestIndex = timei; } } - this->SetTimeStep(minTimeI); // set Modified() if TimeStep changed + this->SetTimeStep(nearestIndex); // set Modified() if TimeStep changed } } +void vtkOpenFOAMReaderPrivate::PrintTimes(std::ostream& os, vtkIndent indent, bool full) const +{ + const vtkIdType nTimes = this->TimeNames->GetNumberOfTuples(); + + os << indent << "Times: " << nTimes << " ("; + if ((nTimes > 5) && !full) + { + os << this->TimeNames->GetValue(0) << ' ' << this->TimeNames->GetValue(1) << " .. " + << this->TimeNames->GetValue(nTimes - 1); + } + else + { + for (vtkIdType timei = 0; timei < nTimes; ++timei) + { + if (timei) + { + os << ' '; + } + os << this->TimeNames->GetValue(timei); + } + } + os << ')' << endl; + os << indent << "Step: " << this->TimeStep << " ("; + + if (this->TimeStep < 0 || this->TimeStep >= nTimes) + { + os << "n/a"; + } + else + { + os << this->TimeNames->GetValue(this->TimeStep); + } + os << ')' << endl; +} + //------------------------------------------------------------------------------ // Gather the necessary information to create a path to the data bool vtkOpenFOAMReaderPrivate::MakeInformationVector(const std::string& casePath, @@ -5610,6 +5673,53 @@ bool vtkOpenFOAMReaderPrivate::MakeInformationVector(const std::string& casePath return true; } +//------------------------------------------------------------------------------ +bool vtkOpenFOAMReaderPrivate::MakeInformationVector(const std::string& casePath, + const std::string& procName, vtkOpenFOAMReader* parent, vtkStringArray* timeNames, + vtkDoubleArray* timeValues, bool requirePolyMesh) +{ + vtkFoamDebug(<< "MakeInformationVector (" << this->RegionName << "/" << procName + << ") polyMesh:" << requirePolyMesh << " - inherit times\n"); + + this->CasePath = casePath; + this->ProcessorName = procName; + this->Parent = parent; + + this->TimeNames->Delete(); + this->TimeNames = timeNames; + this->TimeNames->Register(nullptr); + this->TimeValues->Delete(); + this->TimeValues = timeValues; + this->TimeValues->Register(nullptr); + + // does not seem to be required even if number of timesteps reduced + // upon refresh since ParaView rewinds TimeStep to 0, but for precaution + const vtkIdType nTimes = this->TimeValues->GetNumberOfTuples(); + if (nTimes) + { + if (this->TimeStep >= nTimes) + { + this->SetTimeStep(static_cast<int>(nTimes - 1)); + } + } + else + { + this->SetTimeStep(0); + } + + // Clear any cached knowledge + this->PolyMeshTimeIndexPoints.clear(); + this->PolyMeshTimeIndexFaces.clear(); + + // Normally expect a (default region) polyMesh/, but not for multi-region cases + if (requirePolyMesh) + { + this->PopulateMeshTimeIndices(); + } + + return true; +} + //------------------------------------------------------------------------------ // Copy time instances information and create mesh times void vtkOpenFOAMReaderPrivate::SetupInformation(const std::string& casePath, @@ -6188,14 +6298,21 @@ int vtkOpenFOAMReaderPrivate::MakeMetaDataAtTimeStep(vtkStringArray* cellSelecti // timestep to add extra objects that don't exist at timestep 0 into // selection lists. Note the ObjectNames array will be recreated in // RequestData() so we don't have to worry about duplicated fields. - if (listNextTimeStep && this->TimeValues->GetNumberOfTuples() >= 2 && this->TimeStep == 0) + + if (listNextTimeStep && this->TimeStep == 0) { - const std::string timePath2(this->TimePath(1)); - this->GetFieldNames(timePath2 + this->RegionPath()); - // if lagrangian clouds were not found at timestep 0 - if (!this->LagrangianPaths->GetNumberOfTuples()) + int nextTimeStep = this->TimeStep + 1; + if (nextTimeStep < this->TimeValues->GetNumberOfTuples()) { - this->LocateLagrangianClouds(timePath2); + timePath = this->TimePath(nextTimeStep); + this->GetFieldNames(timePath + this->RegionPath()); + + // Lagrangian clouds are likely missing at time 0 + // - could also lookahead multiple time steps (if desired) + if (!this->LagrangianPaths->GetNumberOfTuples()) + { + this->LocateLagrangianClouds(timePath); + } } } @@ -6474,8 +6591,8 @@ bool vtkOpenFOAMReaderPrivate::ListTimeDirectoriesByInstances() this->TimeValues->InsertNextValue(0); } - this->TimeValues->Squeeze(); this->TimeNames->Squeeze(); + this->TimeValues->Squeeze(); vtkIdType nTimes = this->TimeValues->GetNumberOfTuples(); @@ -10902,7 +11019,35 @@ void vtkOpenFOAMReader::SetUse64BitFloats(bool val) } //------------------------------------------------------------------------------ -// PrintSelf +// Printing + +void vtkOpenFOAMReader::PrintTimes(ostream& os, vtkIndent indent, bool full) const +{ + os << indent << "TimeInformation (SkipZeroTime: " << this->SkipZeroTime << ")\n"; + this->Readers->InitTraversal(); + for (vtkObject* obj; (obj = this->Readers->GetNextItemAsObject()) != nullptr;) + { + // Is private implementation + { + auto* reader = vtkOpenFOAMReaderPrivate::SafeDownCast(obj); + if (reader) + { + reader->PrintTimes(os, indent.GetNextIndent(), full); + continue; + } + } + // Is sub-reader for derived type + { + auto* reader = vtkOpenFOAMReader::SafeDownCast(obj); + if (reader) + { + reader->PrintTimes(os, indent.GetNextIndent(), full); + continue; + } + } + } +} + void vtkOpenFOAMReader::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); @@ -10913,16 +11058,16 @@ void vtkOpenFOAMReader::PrintSelf(ostream& os, vtkIndent indent) os << indent << "DecomposePolyhedra: " << this->DecomposePolyhedra << endl; os << indent << "PositionsIsIn13Format: " << this->PositionsIsIn13Format << endl; os << indent << "ReadZones: " << this->ReadZones << endl; - os << indent << "SkipZeroTime: " << this->SkipZeroTime << endl; - os << indent << "ListTimeStepsByControlDict: " << this->ListTimeStepsByControlDict << endl; os << indent << "AddDimensionsToArrayNames: " << this->AddDimensionsToArrayNames << endl; + this->PrintTimes(os, indent); + + // PrintSelf for any type of sub-readers this->Readers->InitTraversal(); - vtkObject* reader; - while ((reader = this->Readers->GetNextItemAsObject()) != nullptr) + for (vtkObject* obj; (obj = this->Readers->GetNextItemAsObject()) != nullptr;) { - os << indent << "Reader instance " << static_cast<void*>(reader) << ": \n"; - reader->PrintSelf(os, indent.GetNextIndent()); + os << indent << "Reader instance " << static_cast<void*>(obj) << ": \n"; + obj->PrintSelf(os, indent.GetNextIndent()); } } @@ -11033,19 +11178,19 @@ int vtkOpenFOAMReader::RequestData(vtkInformation* vtkNotUsed(request), // Times { - int nTimes = 0; + int nTimes(0); // Also used for logic double requestedTimeValue(0); if (outInfo->Has(vtkStreamingDemandDrivenPipeline::UPDATE_TIME_STEP())) { nTimes = outInfo->Length(vtkStreamingDemandDrivenPipeline::TIME_STEPS()); - requestedTimeValue = (1 == nTimes - // Only one time-step available, UPDATE_TIME_STEP is unreliable - ? outInfo->Get(vtkStreamingDemandDrivenPipeline::TIME_STEPS(), 0) - : outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_TIME_STEP())); + // UPDATE_TIME_STEP is unreliable if there is only one time-step + requestedTimeValue = + (1 == nTimes ? outInfo->Get(vtkStreamingDemandDrivenPipeline::TIME_STEPS(), 0) + : outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_TIME_STEP())); } - if (nTimes > 0) + if (nTimes) // nTimes also used for logic { outInfo->Set(vtkDataObject::DATA_TIME_STEP(), requestedTimeValue); this->SetTimeValue(requestedTimeValue); @@ -11114,28 +11259,29 @@ int vtkOpenFOAMReader::RequestData(vtkInformation* vtkNotUsed(request), void vtkOpenFOAMReader::SetTimeInformation( vtkInformationVector* outputVector, vtkDoubleArray* timeValues) { + const vtkIdType nTimes = timeValues->GetNumberOfTuples(); + vtkInformation* outInfo = outputVector->GetInformationObject(0); + double timeRange[2]; - if (timeValues->GetNumberOfTuples() > 0) + if (nTimes) { - outputVector->GetInformationObject(0)->Set(vtkStreamingDemandDrivenPipeline::TIME_STEPS(), - timeValues->GetPointer(0), static_cast<int>(timeValues->GetNumberOfTuples())); - timeRange[0] = timeValues->GetValue(0); - timeRange[1] = timeValues->GetValue(timeValues->GetNumberOfTuples() - 1); + timeRange[1] = timeValues->GetValue(nTimes - 1); + + outInfo->Set(vtkStreamingDemandDrivenPipeline::TIME_STEPS(), timeValues->GetPointer(0), + static_cast<int>(nTimes)); } else { timeRange[0] = timeRange[1] = 0.0; - outputVector->GetInformationObject(0)->Set( - vtkStreamingDemandDrivenPipeline::TIME_STEPS(), timeRange, 0); + outInfo->Set(vtkStreamingDemandDrivenPipeline::TIME_STEPS(), timeRange, 0); } - outputVector->GetInformationObject(0)->Set( - vtkStreamingDemandDrivenPipeline::TIME_RANGE(), timeRange, 2); + outInfo->Set(vtkStreamingDemandDrivenPipeline::TIME_RANGE(), timeRange, 2); } //------------------------------------------------------------------------------ -int vtkOpenFOAMReader::MakeInformationVector( - vtkInformationVector* outputVector, const vtkStdString& procName) +int vtkOpenFOAMReader::MakeInformationVector(vtkInformationVector* outputVector, + const vtkStdString& procName, vtkStringArray* timeNames, vtkDoubleArray* timeValues) { this->FileNameOld->assign(this->FileName); @@ -11190,17 +11336,41 @@ int vtkOpenFOAMReader::MakeInformationVector( std::sort(regionNames.begin(), regionNames.end()); } - auto masterReader = vtkSmartPointer<vtkOpenFOAMReaderPrivate>::New(); - if (!masterReader->MakeInformationVector( - casePath, controlDictPath, procName, this->Parent, hasDefaultRegion)) + vtkIdType nTimeNames = 0; + vtkIdType nTimeValues = 0; + + if (((timeNames != nullptr) && (nTimeNames = timeNames->GetNumberOfTuples()) != 0) && + ((timeValues != nullptr) && (nTimeValues = timeValues->GetNumberOfTuples()) != 0) && + (nTimeNames != nTimeValues)) { + vtkErrorMacro(<< "Number of time values " << nTimeValues << " != number of time names " + << nTimeNames); return 0; } - if (masterReader->GetTimeValues()->GetNumberOfTuples() == 0) + auto masterReader = vtkSmartPointer<vtkOpenFOAMReaderPrivate>::New(); + + if (nTimeNames && nTimeNames == nTimeValues) + { + if (!masterReader->MakeInformationVector( + casePath, procName, this->Parent, timeNames, timeValues, hasDefaultRegion)) + { + return 0; + } + } + else + { + if (!masterReader->MakeInformationVector( + casePath, controlDictPath, procName, this->Parent, hasDefaultRegion)) + { + return 0; + } + } + + nTimeValues = masterReader->GetTimeValues()->GetNumberOfTuples(); + if (!nTimeValues) { vtkErrorMacro(<< this->FileName << " contains no timestep data."); - return 0; } if (hasDefaultRegion) @@ -11295,31 +11465,121 @@ void vtkOpenFOAMReader::AddSelectionNames( bool vtkOpenFOAMReader::SetTimeValue(const double timeValue) { bool modified = false; - vtkOpenFOAMReaderPrivate* reader; this->Readers->InitTraversal(); - while ((reader = vtkOpenFOAMReaderPrivate::SafeDownCast(this->Readers->GetNextItemAsObject())) != - nullptr) + for (vtkObject* obj; (obj = this->Readers->GetNextItemAsObject()) != nullptr;) { - vtkMTimeType mTime = reader->GetMTime(); - reader->SetTimeValue(timeValue); - if (reader->GetMTime() != mTime) + // Is private implementation { - modified = true; + auto* reader = vtkOpenFOAMReaderPrivate::SafeDownCast(obj); + if (reader) + { + const vtkMTimeType mTime = reader->GetMTime(); + reader->SetTimeValue(timeValue); + if (reader->GetMTime() != mTime) + { + modified = true; + } + continue; + } + } + // Is sub-reader for derived type + { + auto* reader = vtkOpenFOAMReader::SafeDownCast(obj); + if (reader) + { + if (reader->SetTimeValue(timeValue)) + { + modified = true; + } + continue; + } } } return modified; } +//------------------------------------------------------------------------------ +double vtkOpenFOAMReader::GetTimeValue() const +{ + vtkObject* obj = this->Readers->GetNumberOfItems() ? this->Readers->GetItemAsObject(0) : nullptr; + + if (obj) + { + // Is private implementation + { + auto* reader = vtkOpenFOAMReaderPrivate::SafeDownCast(obj); + if (reader) + { + return reader->GetTimeValue(); + } + } + // Is sub-reader for derived type + { + auto* reader = vtkOpenFOAMReader::SafeDownCast(obj); + if (reader) + { + return reader->GetTimeValue(); + } + } + } + + return 0; +} + +//------------------------------------------------------------------------------ +vtkStringArray* vtkOpenFOAMReader::GetTimeNames() +{ + vtkObject* obj = this->Readers->GetNumberOfItems() ? this->Readers->GetItemAsObject(0) : nullptr; + + if (obj) + { + // Is private implementation + { + auto* reader = vtkOpenFOAMReaderPrivate::SafeDownCast(obj); + if (reader) + { + return reader->GetTimeNames(); + } + } + // Is sub-reader for derived type + { + auto* reader = vtkOpenFOAMReader::SafeDownCast(obj); + if (reader) + { + return reader->GetTimeNames(); + } + } + } + + return nullptr; +} + //------------------------------------------------------------------------------ vtkDoubleArray* vtkOpenFOAMReader::GetTimeValues() { - if (this->Readers->GetNumberOfItems() <= 0) + vtkObject* obj = this->Readers->GetNumberOfItems() ? this->Readers->GetItemAsObject(0) : nullptr; + + if (obj) { - return nullptr; + // Is private implementation + { + auto* reader = vtkOpenFOAMReaderPrivate::SafeDownCast(obj); + if (reader) + { + return reader->GetTimeValues(); + } + } + // Is sub-reader for derived type + { + auto* reader = vtkOpenFOAMReader::SafeDownCast(obj); + if (reader) + { + return reader->GetTimeValues(); + } + } } - vtkOpenFOAMReaderPrivate* reader = - vtkOpenFOAMReaderPrivate::SafeDownCast(this->Readers->GetItemAsObject(0)); - return reader != nullptr ? reader->GetTimeValues() : nullptr; + + return nullptr; } //------------------------------------------------------------------------------ diff --git a/IO/Geometry/vtkOpenFOAMReader.h b/IO/Geometry/vtkOpenFOAMReader.h index 4a0ef47c18193ec1f8f0153e5efea6fe45e96703..555d27f004be49ffb75e514aa258339148c3f45d 100644 --- a/IO/Geometry/vtkOpenFOAMReader.h +++ b/IO/Geometry/vtkOpenFOAMReader.h @@ -62,6 +62,9 @@ class vtkOpenFOAMReaderPrivate; class VTKIOGEOMETRY_EXPORT vtkOpenFOAMReader : public vtkMultiBlockDataSetAlgorithm { public: + // Access for implementation class + friend class vtkOpenFOAMReaderPrivate; + static vtkOpenFOAMReader* New(); vtkTypeMacro(vtkOpenFOAMReader, vtkMultiBlockDataSetAlgorithm); void PrintSelf(ostream& os, vtkIndent indent) override; @@ -343,12 +346,16 @@ public: } void SetParent(vtkOpenFOAMReader* parent) { this->Parent = parent; } - int MakeInformationVector(vtkInformationVector*, const vtkStdString&); + + int MakeInformationVector(vtkInformationVector*, const vtkStdString& procDirName, + vtkStringArray* timeNames = nullptr, vtkDoubleArray* timeValues = nullptr); + + double GetTimeValue() const; bool SetTimeValue(const double); + vtkStringArray* GetTimeNames(); vtkDoubleArray* GetTimeValues(); - int MakeMetaDataAtTimeStep(const bool); - friend class vtkOpenFOAMReaderPrivate; + int MakeMetaDataAtTimeStep(const bool); protected: // refresh flag @@ -450,6 +457,9 @@ private: void EnableAllSelectionArrays(vtkDataArraySelection*); void AddSelectionNames(vtkDataArraySelection*, vtkStringArray*); + + // Print some time information (names, current time-step) + void PrintTimes(std::ostream& os, vtkIndent indent = vtkIndent(), bool full = false) const; }; #endif diff --git a/IO/Parallel/vtkPOpenFOAMReader.cxx b/IO/Parallel/vtkPOpenFOAMReader.cxx index 5b19002102baf0b59aefb9ae2f40ec544ceb13aa..9ffa2495b8572622b8e7860a9acf6688e20dbad5 100644 --- a/IO/Parallel/vtkPOpenFOAMReader.cxx +++ b/IO/Parallel/vtkPOpenFOAMReader.cxx @@ -471,6 +471,7 @@ int vtkPOpenFOAMReader::RequestInformation( int nProcessorDirs = 0; auto processorDirs = vtkSmartPointer<vtkIntArray>::New(); + vtkStringArray* timeNames; vtkDoubleArray* timeValues; int ret = 1; @@ -503,16 +504,19 @@ int vtkPOpenFOAMReader::RequestInformation( return 0; } this->Superclass::Readers->AddItem(masterReader); + timeNames = masterReader->GetTimeNames(); timeValues = masterReader->GetTimeValues(); } else { + timeNames = vtkStringArray::New(); timeValues = vtkDoubleArray::New(); this->Superclass::SetTimeInformation(outputVector, timeValues); } } else { + timeNames = vtkStringArray::New(); timeValues = vtkDoubleArray::New(); } @@ -529,6 +533,7 @@ int vtkPOpenFOAMReader::RequestInformation( this->Controller->Broadcast(processorDirs, 0); this->Controller->Broadcast(timeValues, 0); + this->Broadcast(timeNames); if (this->ProcessId != 0) { this->Superclass::SetTimeInformation(outputVector, timeValues); @@ -548,7 +553,7 @@ int vtkPOpenFOAMReader::RequestInformation( auto subReader = ::NewFoamReader(this); // If getting metadata failed, simply skip the reader instance - if (subReader->MakeInformationVector(nullptr, procDirName) && + if (subReader->MakeInformationVector(nullptr, procDirName, timeNames, timeValues) && subReader->MakeMetaDataAtTimeStep(true)) { this->Superclass::Readers->AddItem(subReader); @@ -562,6 +567,7 @@ int vtkPOpenFOAMReader::RequestInformation( // Cleanup if (this->ProcessId != 0 || (nProcessorDirs == 0)) { + timeNames->Delete(); timeValues->Delete(); } @@ -613,18 +619,25 @@ int vtkPOpenFOAMReader::RequestData( int ret = 1; if (this->Superclass::Readers->GetNumberOfItems()) { - int nSteps = 0; + int nTimes(0); // Also used for logic double requestedTimeValue(0); if (outInfo->Has(vtkStreamingDemandDrivenPipeline::UPDATE_TIME_STEP())) { - requestedTimeValue = outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_TIME_STEP()); - nSteps = outInfo->Length(vtkStreamingDemandDrivenPipeline::TIME_STEPS()); - if (nSteps > 0) + nTimes = outInfo->Length(vtkStreamingDemandDrivenPipeline::TIME_STEPS()); + + // UPDATE_TIME_STEP is unreliable if there is only one time-step + requestedTimeValue = + (1 == nTimes ? outInfo->Get(vtkStreamingDemandDrivenPipeline::TIME_STEPS(), 0) + : outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_TIME_STEP())); + + if (nTimes) { outInfo->Set(vtkDataObject::DATA_TIME_STEP(), requestedTimeValue); } } + // NOTE: do not call SetTimeValue() directly here + vtkAppendCompositeDataLeaves* append = vtkAppendCompositeDataLeaves::New(); // append->AppendFieldDataOn(); @@ -637,8 +650,8 @@ int vtkPOpenFOAMReader::RequestData( // even if the child readers themselves are not modified, mark // them as modified if "this" has been modified, since they // refer to the property of "this" - if ((nSteps > 0 && reader->SetTimeValue(requestedTimeValue)) || - this->MTimeOld != this->GetMTime()) + if ((nTimes && reader->SetTimeValue(requestedTimeValue)) || + (this->MTimeOld != this->GetMTime())) { reader->Modified(); }