mirror of
https://gitlab.kitware.com/vtk/vtk-m
synced 2024-10-06 10:29:00 +00:00
382 lines
15 KiB
C++
382 lines
15 KiB
C++
//============================================================================
|
|
// 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.
|
|
//
|
|
// Copyright 2014 National Technology & Engineering Solutions of Sandia, LLC (NTESS).
|
|
// Copyright 2014 UT-Battelle, LLC.
|
|
// Copyright 2014 Los Alamos National Security.
|
|
//
|
|
// Under the terms of Contract DE-NA0003525 with NTESS,
|
|
// the U.S. Government retains certain rights in this software.
|
|
//
|
|
// Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National
|
|
// Laboratory (LANL), the U.S. Government retains certain rights in
|
|
// this software.
|
|
//============================================================================
|
|
#ifndef vtk_m_worklet_particleadvection_TemporalGridEvaluators_h
|
|
#define vtk_m_worklet_particleadvection_TemporalGridEvaluators_h
|
|
|
|
#include <vtkm/Types.h>
|
|
#include <vtkm/VectorAnalysis.h>
|
|
#include <vtkm/cont/ArrayHandle.h>
|
|
#include <vtkm/cont/CellSetStructured.h>
|
|
#include <vtkm/cont/DataSet.h>
|
|
#include <vtkm/cont/DeviceAdapter.h>
|
|
#include <vtkm/worklet/particleadvection/Particles.h>
|
|
|
|
namespace vtkm
|
|
{
|
|
namespace worklet
|
|
{
|
|
namespace particleadvection
|
|
{
|
|
|
|
template <typename FieldArrayType>
|
|
class TemporalGridEvaluator : public vtkm::cont::ExecutionObjectBase
|
|
{
|
|
using ScalarType = vtkm::worklet::particleadvection::ScalarType;
|
|
|
|
public:
|
|
TemporalGridEvaluator() = default;
|
|
|
|
VTKM_CONT
|
|
TemporalGridEvaluator(const vtkm::cont::CoordinateSystem& coords1,
|
|
const vtkm::cont::DynamicCellSet& cellSet1,
|
|
const FieldArrayType& vectorField1,
|
|
const ScalarType datasettime1,
|
|
const vtkm::cont::CoordinateSystem& coords2,
|
|
const vtkm::cont::DynamicCellSet& cellSet2,
|
|
const FieldArrayType& vectorField2,
|
|
const ScalarType datasettime2)
|
|
: Coords1(coords1)
|
|
, CellSet1(cellSet1)
|
|
, VectorField1(vectorField1)
|
|
, DataSetTime1(datasettime1)
|
|
, Coords2(coords2)
|
|
, CellSet2(cellSet2)
|
|
, VectorField2(vectorField2)
|
|
, DataSetTime2(datasettime2)
|
|
{
|
|
}
|
|
|
|
public:
|
|
template <typename DeviceAdapterTag>
|
|
class ExecObject
|
|
{
|
|
using PortalType =
|
|
typename FieldArrayType::template ExecutionTypes<DeviceAdapterTag>::PortalConst;
|
|
|
|
public:
|
|
/*
|
|
* This method is intended to update the initial datasets we are going to
|
|
* use to advect the particels.
|
|
* The components are similar to the normal datasets except now we add times
|
|
* for which the datasets were active to perform temporal interpolation.
|
|
*/
|
|
VTKM_CONT
|
|
ExecObject(const vtkm::cont::CoordinateSystem& coords1,
|
|
const vtkm::cont::DynamicCellSet& cellSet1,
|
|
const FieldArrayType& vectorField1,
|
|
const ScalarType datasettime1,
|
|
const vtkm::cont::CoordinateSystem& coords2,
|
|
const vtkm::cont::DynamicCellSet& cellSet2,
|
|
const FieldArrayType& vectorField2,
|
|
const ScalarType datasettime2)
|
|
|
|
{
|
|
using UniformType = vtkm::cont::ArrayHandleUniformPointCoordinates;
|
|
using StructuredType = vtkm::cont::CellSetStructured<3>;
|
|
|
|
if (!coords1.GetData().IsType<UniformType>() || !coords2.GetData().IsType<UniformType>())
|
|
throw vtkm::cont::ErrorInternal("Coordinates are not uniform.");
|
|
if (!cellSet1.IsSameType(StructuredType()) || !cellSet2.IsSameType(StructuredType()))
|
|
throw vtkm::cont::ErrorInternal("Cells are not 3D structured.");
|
|
|
|
vectors1 = vectorField1.PrepareForInput(DeviceAdapterTag());
|
|
vectors2 = vectorField2.PrepareForInput(DeviceAdapterTag());
|
|
|
|
bounds1 = coords1.GetBounds();
|
|
bounds2 = coords2.GetBounds();
|
|
|
|
vtkm::cont::CellSetStructured<3> cells1;
|
|
vtkm::cont::CellSetStructured<3> cells2;
|
|
|
|
cellSet1.CopyTo(cells1);
|
|
dims1 = cells1.GetSchedulingRange(vtkm::TopologyElementTagPoint());
|
|
|
|
cellSet2.CopyTo(cells2);
|
|
dims2 = cells2.GetSchedulingRange(vtkm::TopologyElementTagPoint());
|
|
|
|
time1 = datasettime1;
|
|
time2 = datasettime2;
|
|
|
|
planeSize1 = dims1[1] * dims1[1];
|
|
rowSize1 = dims1[0];
|
|
|
|
planeSize2 = dims2[1] * dims2[1];
|
|
rowSize2 = dims2[0];
|
|
|
|
scale1[0] = static_cast<ScalarType>(dims1[0] - 1) /
|
|
static_cast<ScalarType>(bounds1.X.Max - bounds1.X.Min);
|
|
scale1[1] = static_cast<ScalarType>(dims1[1] - 1) /
|
|
static_cast<ScalarType>(bounds1.Y.Max - bounds1.Y.Min);
|
|
scale1[2] = static_cast<ScalarType>(dims1[2] - 1) /
|
|
static_cast<ScalarType>(bounds1.Z.Max - bounds1.Z.Min);
|
|
|
|
scale2[0] = static_cast<ScalarType>(dims2[0] - 1) /
|
|
static_cast<ScalarType>(bounds2.X.Max - bounds2.X.Min);
|
|
scale2[1] = static_cast<ScalarType>(dims2[1] - 1) /
|
|
static_cast<ScalarType>(bounds2.Y.Max - bounds2.Y.Min);
|
|
scale2[2] = static_cast<ScalarType>(dims2[2] - 1) /
|
|
static_cast<ScalarType>(bounds2.Z.Max - bounds2.Z.Min);
|
|
}
|
|
|
|
|
|
VTKM_EXEC_CONT
|
|
bool IsWithinSpatialBoundary(const vtkm::Vec<ScalarType, 3>& position) const
|
|
{
|
|
if (!bounds1.Contains(position) || !bounds2.Contains(position))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
VTKM_EXEC_CONT
|
|
bool IsWithinTemporalBoundary(const ScalarType time) const
|
|
{
|
|
if (time < time1 || time >= time2)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
VTKM_EXEC_CONT
|
|
void GetSpatialBoundary(vtkm::Vec<ScalarType, 3>& dir, vtkm::Vec<ScalarType, 3>& boundary) const
|
|
{
|
|
// Based on the direction of the velocity we need to be able to tell where
|
|
// the particle will exit the domain from to actually push it out of domain.
|
|
boundary[0] = static_cast<ScalarType>(dir[0] > 0 ? bounds2.X.Max : bounds2.X.Min);
|
|
boundary[1] = static_cast<ScalarType>(dir[1] > 0 ? bounds2.Y.Max : bounds2.Y.Min);
|
|
boundary[2] = static_cast<ScalarType>(dir[2] > 0 ? bounds2.Z.Max : bounds2.Z.Min);
|
|
}
|
|
|
|
VTKM_EXEC_CONT
|
|
void GetTemporalBoundary(ScalarType& boundary) const
|
|
{
|
|
// Return the time of the newest time slice
|
|
boundary = time2;
|
|
}
|
|
|
|
/*
|
|
* This method is intended to swap the initial datsets, by adding a new
|
|
* dataset. The first dataset is replaced by the second, also the corresponding
|
|
* times. The second dataset is replaced by a new dataset.
|
|
*/
|
|
VTKM_CONT
|
|
void UpdateDataSetForNewTimeSlice(const vtkm::cont::CoordinateSystem& coords,
|
|
const vtkm::cont::DynamicCellSet& cellSet,
|
|
const FieldArrayType& vectorField,
|
|
const ScalarType time)
|
|
{
|
|
using UniformType = vtkm::cont::ArrayHandleUniformPointCoordinates;
|
|
using StructuredType = vtkm::cont::CellSetStructured<3>;
|
|
if (!coords.GetData().IsType<UniformType>())
|
|
throw vtkm::cont::ErrorInternal("Coordinates are not uniform.");
|
|
if (!cellSet.IsSameType(StructuredType()))
|
|
throw vtkm::cont::ErrorInternal("Cells are not 3D structured.");
|
|
vectors1 = vectors2;
|
|
bounds1 = bounds2;
|
|
dims1 = dims2;
|
|
time1 = time2;
|
|
planeSize1 = planeSize2;
|
|
rowSize1 = rowSize2;
|
|
scale1[0] = scale2[0];
|
|
scale1[1] = scale2[1];
|
|
scale1[2] = scale2[2];
|
|
|
|
vectors2 = vectorField.PrepareForInput(DeviceAdapterTag());
|
|
bounds2 = coords.GetBounds();
|
|
vtkm::cont::CellSetStructured<3> cells;
|
|
cellSet.CopyTo(cells);
|
|
dims2 = cells.GetSchedulingRange(vtkm::TopologyElementTagPoint());
|
|
time2 = time;
|
|
planeSize2 = dims2[1] * dims2[1];
|
|
rowSize2 = dims2[0];
|
|
|
|
scale2[0] = static_cast<ScalarType>(dims2[0] - 1) /
|
|
static_cast<ScalarType>(bounds2.X.Max - bounds2.X.Min);
|
|
scale2[1] = static_cast<ScalarType>(dims2[1] - 1) /
|
|
static_cast<ScalarType>(bounds2.Y.Max - bounds2.Y.Min);
|
|
scale2[2] = static_cast<ScalarType>(dims2[2] - 1) /
|
|
static_cast<ScalarType>(bounds2.Z.Max - bounds2.Z.Min);
|
|
}
|
|
|
|
VTKM_EXEC
|
|
bool Interpolate(const vtkm::Vec<ScalarType, 3>& position,
|
|
vtkm::Vec<ScalarType, 3>& velocity,
|
|
const PortalType& vectors,
|
|
const vtkm::Bounds& bounds,
|
|
const vtkm::Id3 dims,
|
|
const vtkm::Id& planeSize,
|
|
const vtkm::Id& rowSize,
|
|
const vtkm::Vec<ScalarType, 3>& scale) const
|
|
{
|
|
if (!bounds.Contains(position))
|
|
return false;
|
|
//Set the indices for the interpolation.
|
|
vtkm::Id3 idx000, idx001, idx010, idx011, idx100, idx101, idx110, idx111;
|
|
|
|
vtkm::Vec<ScalarType, 3> normalized =
|
|
vtkm::Vec<ScalarType, 3>((position[0] - static_cast<ScalarType>(bounds.X.Min)) * scale[0],
|
|
(position[1] - static_cast<ScalarType>(bounds.Y.Min)) * scale[1],
|
|
(position[2] - static_cast<ScalarType>(bounds.Z.Min)) * scale[2]);
|
|
|
|
idx000[0] = static_cast<vtkm::IdComponent>(floor(normalized[0]));
|
|
idx000[1] = static_cast<vtkm::IdComponent>(floor(normalized[1]));
|
|
idx000[2] = static_cast<vtkm::IdComponent>(floor(normalized[2]));
|
|
|
|
idx001 = idx000;
|
|
idx001[0] = (idx001[0] + 1) <= dims[0] - 1 ? idx001[0] + 1 : dims[0] - 1;
|
|
idx010 = idx000;
|
|
idx010[1] = (idx010[1] + 1) <= dims[1] - 1 ? idx010[1] + 1 : dims[1] - 1;
|
|
idx011 = idx010;
|
|
idx011[0] = (idx011[0] + 1) <= dims[0] - 1 ? idx011[0] + 1 : dims[0] - 1;
|
|
idx100 = idx000;
|
|
idx100[2] = (idx100[2] + 1) <= dims[2] - 1 ? idx100[2] + 1 : dims[2] - 1;
|
|
idx101 = idx100;
|
|
idx101[0] = (idx101[0] + 1) <= dims[0] - 1 ? idx101[0] + 1 : dims[0] - 1;
|
|
idx110 = idx100;
|
|
idx110[1] = (idx110[1] + 1) <= dims[1] - 1 ? idx110[1] + 1 : dims[1] - 1;
|
|
idx111 = idx110;
|
|
idx111[0] = (idx111[0] + 1) <= dims[0] - 1 ? idx111[0] + 1 : dims[0] - 1;
|
|
|
|
// Get the vecdata at the eight corners
|
|
vtkm::Vec<ScalarType, 3> v000, v001, v010, v011, v100, v101, v110, v111;
|
|
v000 = vectors.Get(idx000[2] * planeSize + idx000[1] * rowSize + idx000[0]);
|
|
v001 = vectors.Get(idx001[2] * planeSize + idx001[1] * rowSize + idx001[0]);
|
|
v010 = vectors.Get(idx010[2] * planeSize + idx010[1] * rowSize + idx010[0]);
|
|
v011 = vectors.Get(idx011[2] * planeSize + idx011[1] * rowSize + idx011[0]);
|
|
v100 = vectors.Get(idx100[2] * planeSize + idx100[1] * rowSize + idx100[0]);
|
|
v101 = vectors.Get(idx101[2] * planeSize + idx101[1] * rowSize + idx101[0]);
|
|
v110 = vectors.Get(idx110[2] * planeSize + idx110[1] * rowSize + idx110[0]);
|
|
v111 = vectors.Get(idx111[2] * planeSize + idx111[1] * rowSize + idx111[0]);
|
|
|
|
// Interpolation in X
|
|
vtkm::Vec<ScalarType, 3> v00, v01, v10, v11;
|
|
ScalarType a = normalized[0] - static_cast<ScalarType>(floor(normalized[0]));
|
|
v00[0] = (1.0f - a) * v000[0] + a * v001[0];
|
|
v00[1] = (1.0f - a) * v000[1] + a * v001[1];
|
|
v00[2] = (1.0f - a) * v000[2] + a * v001[2];
|
|
|
|
v01[0] = (1.0f - a) * v010[0] + a * v011[0];
|
|
v01[1] = (1.0f - a) * v010[1] + a * v011[1];
|
|
v01[2] = (1.0f - a) * v010[2] + a * v011[2];
|
|
|
|
v10[0] = (1.0f - a) * v100[0] + a * v101[0];
|
|
v10[1] = (1.0f - a) * v100[1] + a * v101[1];
|
|
v10[2] = (1.0f - a) * v100[2] + a * v101[2];
|
|
|
|
v11[0] = (1.0f - a) * v110[0] + a * v111[0];
|
|
v11[1] = (1.0f - a) * v110[1] + a * v111[1];
|
|
v11[2] = (1.0f - a) * v110[2] + a * v111[2];
|
|
|
|
// Interpolation in Y
|
|
vtkm::Vec<ScalarType, 3> v0, v1;
|
|
a = normalized[1] - static_cast<ScalarType>(floor(normalized[1]));
|
|
v0[0] = (1.0f - a) * v00[0] + a * v01[0];
|
|
v0[1] = (1.0f - a) * v00[1] + a * v01[1];
|
|
v0[2] = (1.0f - a) * v00[2] + a * v01[2];
|
|
|
|
v1[0] = (1.0f - a) * v10[0] + a * v11[0];
|
|
v1[1] = (1.0f - a) * v10[1] + a * v11[1];
|
|
v1[2] = (1.0f - a) * v10[2] + a * v11[2];
|
|
|
|
a = normalized[2] - static_cast<ScalarType>(floor(normalized[2]));
|
|
velocity[0] = (1.0f - a) * v0[0] + a * v1[0];
|
|
velocity[1] = (1.0f - a) * v0[1] + a * v1[1];
|
|
velocity[2] = (1.0f - a) * v0[2] + a * v1[2];
|
|
return true;
|
|
}
|
|
|
|
VTKM_EXEC
|
|
bool Evaluate(const vtkm::Vec<ScalarType, 3>& position,
|
|
const ScalarType particleTime,
|
|
vtkm::Vec<ScalarType, 3>& velocity) const
|
|
{
|
|
vtkm::Vec<ScalarType, 3> velocity1;
|
|
bool result;
|
|
result =
|
|
Interpolate(position, velocity1, vectors1, bounds1, dims1, planeSize1, rowSize1, scale1);
|
|
if (!result)
|
|
return false;
|
|
|
|
vtkm::Vec<ScalarType, 3> velocity2;
|
|
result =
|
|
Interpolate(position, velocity2, vectors2, bounds2, dims2, planeSize1, rowSize1, scale2);
|
|
if (!result)
|
|
return false;
|
|
|
|
ScalarType proportion = (particleTime - time1) / (time2 - time1);
|
|
|
|
velocity[0] = (1.0f - proportion) * velocity1[0] + proportion * velocity2[0];
|
|
velocity[1] = (1.0f - proportion) * velocity1[1] + proportion * velocity2[1];
|
|
velocity[2] = (1.0f - proportion) * velocity1[2] + proportion * velocity2[2];
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
ScalarType time1, time2;
|
|
vtkm::Vec<ScalarType, 3> scale1, scale2;
|
|
/*
|
|
* Currently only adding functionality to work with unifrom grids.
|
|
* Reason being they are easy to work with.
|
|
*/
|
|
vtkm::Bounds bounds1;
|
|
vtkm::Bounds bounds2;
|
|
vtkm::Id3 dims1;
|
|
vtkm::Id3 dims2;
|
|
/*
|
|
* The resolution of the different slices of data may be
|
|
* different, but needs to be uniform.
|
|
*/
|
|
vtkm::Id planeSize1, planeSize2;
|
|
vtkm::Id rowSize1, rowSize2;
|
|
/*
|
|
* These are the portals that contain the actual data for the interpolation.
|
|
*/
|
|
PortalType vectors1;
|
|
PortalType vectors2;
|
|
};
|
|
|
|
template <typename DeviceAdapter>
|
|
VTKM_CONT ExecObject<DeviceAdapter> PrepareForExecution(DeviceAdapter) const
|
|
{
|
|
return ExecObject<DeviceAdapter>(this->Coords1,
|
|
this->CellSet1,
|
|
this->VectorField1,
|
|
this->DataSetTime1,
|
|
this->Coords2,
|
|
this->CellSet2,
|
|
this->VectorField2,
|
|
this->DataSetTime2);
|
|
}
|
|
|
|
private:
|
|
vtkm::cont::CoordinateSystem Coords1;
|
|
vtkm::cont::DynamicCellSet CellSet1;
|
|
FieldArrayType VectorField1;
|
|
ScalarType DataSetTime1;
|
|
vtkm::cont::CoordinateSystem Coords2;
|
|
vtkm::cont::DynamicCellSet CellSet2;
|
|
FieldArrayType VectorField2;
|
|
ScalarType DataSetTime2;
|
|
};
|
|
|
|
} //namespace particleadvection
|
|
} //namespace worklet
|
|
} //namespace vtkm
|
|
|
|
#endif //vtk_m_worklet_particleadvection_TemporalGridEvaluators_h
|