//============================================================================ // Copyright (c) Kitware, Inc. // All rights reserved. // See LICENSE.txt for details. // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notice for more information. //============================================================================ #ifndef vtk_m_filter_LagrangianStructures_hxx #define vtk_m_filter_LagrangianStructures_hxx #include #include #include #include #include #include #include #include #include #include namespace vtkm { namespace filter { namespace detail { class ExtractParticlePosition : public vtkm::worklet::WorkletMapField { public: using ControlSignature = void(FieldIn particle, FieldOut position); using ExecutionSignature = void(_1, _2); using InputDomain = _1; VTKM_EXEC void operator()(const vtkm::Particle& particle, vtkm::Vec3f& pt) const { pt = particle.Pos; } }; class MakeParticles : public vtkm::worklet::WorkletMapField { public: using ControlSignature = void(FieldIn seed, FieldOut particle); using ExecutionSignature = void(WorkIndex, _1, _2); using InputDomain = _1; VTKM_EXEC void operator()(const vtkm::Id index, const vtkm::Vec3f& seed, vtkm::Particle& particle) const { particle.ID = index; particle.Pos = seed; } }; } //detail //----------------------------------------------------------------------------- inline VTKM_CONT LagrangianStructures::LagrangianStructures() : vtkm::filter::FilterDataSetWithField() { OutputFieldName = std::string("FTLE"); } //----------------------------------------------------------------------------- template inline VTKM_CONT vtkm::cont::DataSet LagrangianStructures::DoExecute( const vtkm::cont::DataSet& input, const vtkm::cont::ArrayHandle, StorageType>& field, const vtkm::filter::FieldMetadata& fieldMeta, const vtkm::filter::PolicyBase&) { if (!fieldMeta.IsPointField()) { throw vtkm::cont::ErrorFilterExecution("Point field expected."); } using Structured2DType = vtkm::cont::CellSetStructured<2>; using Structured3DType = vtkm::cont::CellSetStructured<3>; using FieldHandle = vtkm::cont::ArrayHandle, StorageType>; using FieldType = vtkm::worklet::particleadvection::VelocityField; using GridEvaluator = vtkm::worklet::particleadvection::GridEvaluator; using IntegratorType = vtkm::worklet::particleadvection::RK4Integrator; using Stepper = vtkm::worklet::particleadvection::Stepper; vtkm::FloatDefault stepSize = this->GetStepSize(); vtkm::Id numberOfSteps = this->GetNumberOfSteps(); vtkm::cont::CoordinateSystem coordinates = input.GetCoordinateSystem(); vtkm::cont::DynamicCellSet cellset = input.GetCellSet(); vtkm::cont::DataSet lcsInput; if (this->GetUseAuxiliaryGrid()) { vtkm::Id3 lcsGridDims = this->GetAuxiliaryGridDimensions(); vtkm::Bounds inputBounds = coordinates.GetBounds(); vtkm::Vec3f origin(static_cast(inputBounds.X.Min), static_cast(inputBounds.Y.Min), static_cast(inputBounds.Z.Min)); vtkm::Vec3f spacing; spacing[0] = static_cast(inputBounds.X.Length()) / static_cast(lcsGridDims[0] - 1); spacing[1] = static_cast(inputBounds.Y.Length()) / static_cast(lcsGridDims[1] - 1); spacing[2] = static_cast(inputBounds.Z.Length()) / static_cast(lcsGridDims[2] - 1); vtkm::cont::DataSetBuilderUniform uniformDatasetBuilder; lcsInput = uniformDatasetBuilder.Create(lcsGridDims, origin, spacing); } else { // Check if input dataset is structured. // If not, we cannot proceed. if (!(cellset.IsType() || cellset.IsType())) throw vtkm::cont::ErrorFilterExecution( "Provided data is not structured, provide parameters for an auxiliary grid."); lcsInput = input; } vtkm::cont::ArrayHandle lcsInputPoints, lcsOutputPoints; vtkm::cont::ArrayCopy(lcsInput.GetCoordinateSystem().GetData(), lcsInputPoints); if (this->GetUseFlowMapOutput()) { // Check if there is a 1:1 correspondense between the flow map // and the input points lcsOutputPoints = this->GetFlowMapOutput(); if (lcsInputPoints.GetNumberOfValues() != lcsOutputPoints.GetNumberOfValues()) throw vtkm::cont::ErrorFilterExecution( "Provided flow map does not correspond to the input points for LCS filter."); } else { vtkm::cont::Invoker invoke; FieldType velocities(field); GridEvaluator evaluator(input.GetCoordinateSystem(), input.GetCellSet(), velocities); Stepper integrator(evaluator, stepSize); vtkm::worklet::ParticleAdvection particles; vtkm::worklet::ParticleAdvectionResult advectionResult; vtkm::cont::ArrayHandle advectionPoints; invoke(detail::MakeParticles{}, lcsInputPoints, advectionPoints); advectionResult = particles.Run(integrator, advectionPoints, numberOfSteps); invoke(detail::ExtractParticlePosition{}, advectionResult.Particles, lcsOutputPoints); } // FTLE output field vtkm::cont::ArrayHandle outputField; vtkm::FloatDefault advectionTime = this->GetAdvectionTime(); vtkm::cont::DynamicCellSet lcsCellSet = lcsInput.GetCellSet(); if (lcsCellSet.IsType()) { using AnalysisType = vtkm::worklet::LagrangianStructures<2>; AnalysisType ftleCalculator(advectionTime, lcsCellSet); vtkm::worklet::DispatcherMapField dispatcher(ftleCalculator); dispatcher.Invoke(lcsInputPoints, lcsOutputPoints, outputField); } else if (lcsCellSet.IsType()) { using AnalysisType = vtkm::worklet::LagrangianStructures<3>; AnalysisType ftleCalculator(advectionTime, lcsCellSet); vtkm::worklet::DispatcherMapField dispatcher(ftleCalculator); dispatcher.Invoke(lcsInputPoints, lcsOutputPoints, outputField); } vtkm::cont::DataSet output; output.AddCoordinateSystem(lcsInput.GetCoordinateSystem()); output.SetCellSet(lcsInput.GetCellSet()); output.AddPointField(this->GetOutputFieldName(), outputField); return output; } //----------------------------------------------------------------------------- template inline VTKM_CONT bool LagrangianStructures::MapFieldOntoOutput( vtkm::cont::DataSet& result, const vtkm::cont::Field& field, vtkm::filter::PolicyBase) { if (field.IsFieldGlobal()) { result.AddField(field); return true; } else { return false; } } } } // namespace vtkm::filter #endif