diff --git a/Clients/ParaView/Testing/Data/Baseline/TableFFT1.png.sha512 b/Clients/ParaView/Testing/Data/Baseline/TableFFT1.png.sha512 index e2979f1687a..3a7540c1fa7 100644 --- a/Clients/ParaView/Testing/Data/Baseline/TableFFT1.png.sha512 +++ b/Clients/ParaView/Testing/Data/Baseline/TableFFT1.png.sha512 @@ -1 +1 @@ -183e7d3a8d12b137501347a604682898b8c702ee65f531bd309955eecee4715aebeca91c7dcf7b29c68804376187c2223b777c4c7e4a5f50676e57d5c2a5c356 +19b1416d47a2e25c28e35e773f1b444dfd7763a2e16f287992383b3b657af89d74e67e750415217a6f713c343e0ec35a0e508691c58707d56645a4b42182632a diff --git a/Clients/ParaView/Testing/Data/Baseline/TableFFT2.png.sha512 b/Clients/ParaView/Testing/Data/Baseline/TableFFT2.png.sha512 index 672398b511b..e2451348a86 100644 --- a/Clients/ParaView/Testing/Data/Baseline/TableFFT2.png.sha512 +++ b/Clients/ParaView/Testing/Data/Baseline/TableFFT2.png.sha512 @@ -1 +1 @@ -da0cc9e935afbea212c2795bcb7b292c77376b497edfd1ef335b480a42f755684ee7a8a11e9cf509a2045b37e499717482208916d37571145cee483a55f55c77 +3c3b63fa16bad315c21d8a4d2d7aca11e13057752f3e35497db874533e185621a73b724f04957b990891b7a36aee69e4750160712707a0271ee706e6699d609f diff --git a/Clients/ParaView/Testing/XML/CMakeLists.txt b/Clients/ParaView/Testing/XML/CMakeLists.txt index af8a26775a0..4e4ed43c980 100644 --- a/Clients/ParaView/Testing/XML/CMakeLists.txt +++ b/Clients/ParaView/Testing/XML/CMakeLists.txt @@ -1469,7 +1469,6 @@ if(PARAVIEW_USE_PYTHON) SelectionLinkScripting.xml SelectPointsTrace.xml SplitViewTrace.xml - TestTableFFT.xml TraceMultiViews.xml ) set(SelectCellsTrace_DISABLE_CRS TRUE) @@ -1525,7 +1524,9 @@ if(PARAVIEW_USE_PYTHON) list(APPEND TESTS_WITH_INLINE_COMPARES FindDataTrace.xml - FindDataQueries.xml) + FindDataQueries.xml + TestTableFFT.xml # needs programmable filter + numpy + ) # PythonAlgorithm plugin tests. configure_file( diff --git a/Clients/ParaView/Testing/XML/TestTableFFT.xml b/Clients/ParaView/Testing/XML/TestTableFFT.xml index 533bb3a5013..ea37b157cf4 100644 --- a/Clients/ParaView/Testing/XML/TestTableFFT.xml +++ b/Clients/ParaView/Testing/XML/TestTableFFT.xml @@ -5,27 +5,14 @@ + arguments="time = numpy.linspace(0, 1, 10000, endpoint=False) signal = numpy.sin(2 * numpy.pi * 1000 * time) output.RowData.append(time, "Time") output.RowData.append(signal, "Signal")"/> @@ -36,7 +23,7 @@ if gc.GetLocalProcessId() == 0: - + diff --git a/Remoting/Application/Resources/filters_filtersgeneral.xml b/Remoting/Application/Resources/filters_filtersgeneral.xml index 371dae65e74..2c6612ef24f 100644 --- a/Remoting/Application/Resources/filters_filtersgeneral.xml +++ b/Remoting/Application/Resources/filters_filtersgeneral.xml @@ -2647,8 +2647,19 @@ + + + + If the "Time" column is not found then this value will be used as the sample rate of the input. + + + @@ -2660,14 +2671,13 @@ Specify the windowing function to apply on the input. This allows to better process - data that is not periodic. When NumberOfBlocks > 1, the windowing function is applied - to each block. + data that is not periodic. When Welch method is used, the window is applied to each frame. - @@ -2684,59 +2694,92 @@ default_values="0"> - Specify if the output should be normalized. + Specify if the output should be normalized. Only used if UseWelchMethod is false. + + + - - Specify if the input should be split in multiple blocks to compute - an average fft across all blocks. + Specify if filter should use the Welch / periodogram method. If true the + input will be split in multiple segment to compute an average fft across + all segments / blocks. This can be faster for input with lots of samples + and also remove some noise. - + default_values="1024"> - Specify the number of blocks to use when computing the average fft over - the whole input sample array. If NumberOfBlock == 1, no average is done - and we only compute the fft on the first BlockSize samples of the input data. + Specify the number of samples to use for each block / segment in the Welch method. + This should be a power of 2 in order to achieve better performance. + - - - + + + Specify the number of samples which will overlap between each block / segment. + If value is not in a valid range (ie inferior to 0 or superior to BlockSize) then the + value BlockSize / 2 will be used. Only used if UseWelchMethod is true. + - + default_values="0"> + + + + + + Set what scaling should be used when applying the Welch method. + + + + + - Specify the number of samples to use for each block. This should be a power of 2. - If not, the closest power of two will be used anyway. + Remove trend on each segment before applying the FFT. This is a constant + detrend where the mean of the signal is substracted to the signal. + Only used if UseWelchMethod is true. + + + + + + - + diff --git a/Remoting/ServerManager/vtkSMStateVersionController.cxx b/Remoting/ServerManager/vtkSMStateVersionController.cxx index 37eaa61f131..4a15fb1d309 100644 --- a/Remoting/ServerManager/vtkSMStateVersionController.cxx +++ b/Remoting/ServerManager/vtkSMStateVersionController.cxx @@ -1449,6 +1449,39 @@ struct Process_5_10_to_5_11 } }; +struct Process_5_11_to_5_12 +{ + bool operator()(xml_document& document) { return ConvertTableFFT(document); } + + static bool ConvertTableFFT(xml_document& document) + { + pugi::xpath_node_set xpath_set = + document.select_nodes("//ServerManagerState/Proxy[@group='filters' and @type='TableFFT']"); + + for (auto xpath_node : xpath_set) + { + auto node = xpath_node.node(); + + if (auto averageNode = node.find_child_by_attribute("name", "AverageFft")) + { + averageNode.attribute("name").set_value("UseWelchMethod"); + } + + if (auto optimizeNode = node.find_child_by_attribute("name", "OptimizeForRealInput")) + { + optimizeNode.attribute("name").set_value("OneSidedSpectrum"); + } + + if (auto nblockNode = node.find_child_by_attribute("name", "NumberOfBlock")) + { + node.remove_child(nblockNode); + } + } + + return true; + } +}; + } // end of namespace vtkStandardNewMacro(vtkSMStateVersionController); @@ -1563,6 +1596,13 @@ bool vtkSMStateVersionController::Process(vtkPVXMLElement* parent, vtkSMSession* version = vtkSMVersion(5, 11, 0); } + if (status && (version < vtkSMVersion(5, 12, 0))) + { + Process_5_11_to_5_12 converter; + status = converter(document); + version = vtkSMVersion(5, 12, 0); + } + if (status) { std::ostringstream stream2; diff --git a/Remoting/Views/vtkSMChartSeriesListDomain.cxx b/Remoting/Views/vtkSMChartSeriesListDomain.cxx index 85d2b4c0569..a147f19f944 100644 --- a/Remoting/Views/vtkSMChartSeriesListDomain.cxx +++ b/Remoting/Views/vtkSMChartSeriesListDomain.cxx @@ -152,7 +152,7 @@ int vtkSMChartSeriesListDomain::ReadXMLAttributes(vtkSMProperty* prop, vtkPVXMLE const char** vtkSMChartSeriesListDomain::GetKnownSeriesNames() { static const char* strings_to_check[] = { "bin_extents", "Time", "time", "arc_length", "XArray", - "x_array", nullptr }; + "x_array", "Frequency", nullptr }; return strings_to_check; } //---------------------------------------------------------------------------- diff --git a/Remoting/Views/vtkSMChartSeriesSelectionDomain.cxx b/Remoting/Views/vtkSMChartSeriesSelectionDomain.cxx index 0eef98f2274..5b8b4df9689 100644 --- a/Remoting/Views/vtkSMChartSeriesSelectionDomain.cxx +++ b/Remoting/Views/vtkSMChartSeriesSelectionDomain.cxx @@ -58,7 +58,7 @@ static void InitSeriesVisibilityDefaults() const char* defaults[] = { "^arc_length", "^bin_extents", "^FileId", "^GlobalElementId", "^GlobalNodeId", "^ObjectId", "^object_id", "^Pedigree.*", "^Points_.*", "^Time", "^vtkOriginal.*", "^ids$", "^ids .*", "^vtkValidPointMask", "^N .*", "^X$", "^X .*", "^Y$", - "^Y .*", "^Z$", "^Z .*", "^vtkGhostType$", nullptr }; + "^Y .*", "^Z$", "^Z .*", "^vtkGhostType$", "^Frequency$", nullptr }; for (int cc = 0; defaults[cc] != nullptr; cc++) { SeriesVisibilityDefaults.push_back( diff --git a/Wrapping/Python/paraview/_backwardscompatibilityhelper.py b/Wrapping/Python/paraview/_backwardscompatibilityhelper.py index 2744cf69e80..835c927ec36 100644 --- a/Wrapping/Python/paraview/_backwardscompatibilityhelper.py +++ b/Wrapping/Python/paraview/_backwardscompatibilityhelper.py @@ -407,6 +407,27 @@ def setattr(proxy, pname, value): raise NotSupportedException("'StaticMesh' is obsolete. Use 'MeshOverTime' property of " + proxy.SMProxy.GetXMLName() + " filter instead.") + # 5.11 -> 5.12 breaking changes on "TableFFT" properties + # Renamed AverageFFTperblock into UseWelchMethod + # Renamed OptimizeForRealInput into OneSidedSpectrum + # Removed NumberOfBlock + if proxy.SMProxy and proxy.SMProxy.GetXMLName() == "TableFFT": + isOldVersion = paraview.compatibility.GetVersion() < (5, 12) + if pname == "AverageFFTperblock": + if isOldVersion: + proxy.GetProperty("UseWelchMethod").SetData(value) + raise Continue() + else: + raise NotSupportedException("'AverageFFTperblock' is obsolete. Use 'UseWelchMethod' property instead.") + elif pname == "OptimizeForRealInput": + if isOldVersion: + proxy.GetProperty("OneSidedSpectrum").SetData(value) + raise Continue() + else: + raise NotSupportedException("'OptimizeForRealInput' is obsolete. Use 'OneSidedSpectrum' property instead.") + elif pname == "NumberOfBlock" and not isOldVersion: + raise NotSupportedException("'NumberOfBlock' is obsolete. See 'BlockOverlap' property instead.") + if not hasattr(proxy, pname): raise AttributeError() proxy.__dict__[pname] = value @@ -859,6 +880,25 @@ def getattr(proxy, pname): raise NotSupportedException( "Since ParaView 5.11, 'UseGeometryFilter' has been removed. ") + # 5.11 -> 5.12 breaking changes on "TableFFT" properties + # Renamed AverageFFTperblock into UseWelchMethod + # Renamed OptimizeForRealInput into OneSidedSpectrum + # Removed NumberOfBlock + if proxy.SMProxy and proxy.SMProxy.GetXMLName() == "TableFFT": + isOldVersion = paraview.compatibility.GetVersion() < (5, 12) + if pname == "AverageFFTperblock": + if isOldVersion: + return proxy.GetProperty("UseWelchMethod").GetData() + else: + raise NotSupportedException("'AverageFft' is obsolete. Use 'UseWelchMethod' property instead.") + elif pname == "OptimizeForRealInput": + if isOldVersion: + return proxy.GetProperty("OneSidedSpectrum").GetData() + else: + raise NotSupportedException("'OptimizeForRealInput' is obsolete. Use 'OneSidedSpectrum' property instead.") + elif pname == "NumberOfBlock" and not isOldVersion: + raise NotSupportedException("'NumberOfBlock' is obsolete. See 'BlockOverlap' property instead.") + raise Continue() # Depending on the compatibility version that has been set, older functionalities