From f88922d99bbd2307b0c3104dd26f22b6fb3420b1 Mon Sep 17 00:00:00 2001 From: "Praetorius, Simon" <simon.praetorius@tu-dresden.de> Date: Thu, 16 Apr 2020 12:48:41 +0200 Subject: [PATCH] higher order vtk reader --- doc/triangles_2d_order3.vtu | 103 +++ doc/triangles_3d_order3.vtu | 140 ++++ dune.module | 2 +- dune/vtk/datacollectors/CMakeLists.txt | 2 + .../datacollectors/lagrangedatacollector.hh | 196 ++++++ dune/vtk/datacollectors/test/CMakeLists.txt | 2 + .../test/test-lagrangedatacollector.cc | 119 ++++ dune/vtk/forward.hh | 3 + dune/vtk/gridcreatorinterface.hh | 16 +- dune/vtk/gridcreators/CMakeLists.txt | 1 + .../vtk/gridcreators/continuousgridcreator.hh | 3 +- .../gridcreators/discontinuousgridcreator.hh | 3 +- dune/vtk/gridcreators/lagrangegridcreator.hh | 344 ++++++++++ dune/vtk/utility/CMakeLists.txt | 4 + dune/vtk/utility/lagrangelfecache.hh | 35 + dune/vtk/utility/lagrangepoints.hh | 605 ++++++++++++++++++ dune/vtk/utility/test/CMakeLists.txt | 13 + dune/vtk/utility/test/test-lagrange.cc | 169 +++++ dune/vtk/utility/test/test-lfecache.cc | 56 ++ dune/vtk/vtkfunction.hh | 10 +- dune/vtk/vtkreader.hh | 11 +- dune/vtk/vtkreader.impl.hh | 22 +- dune/vtk/vtktypes.cc | 41 ++ dune/vtk/vtktypes.hh | 15 +- dune/vtk/vtkwriterinterface.hh | 8 +- src/CMakeLists.txt | 11 +- src/datacollector.cc | 27 +- src/lagrangepoints.cc | 68 ++ src/lagrangereader.cc | 95 +++ 29 files changed, 2097 insertions(+), 27 deletions(-) create mode 100644 doc/triangles_2d_order3.vtu create mode 100644 doc/triangles_3d_order3.vtu create mode 100644 dune/vtk/datacollectors/lagrangedatacollector.hh create mode 100644 dune/vtk/datacollectors/test/CMakeLists.txt create mode 100644 dune/vtk/datacollectors/test/test-lagrangedatacollector.cc create mode 100644 dune/vtk/gridcreators/lagrangegridcreator.hh create mode 100644 dune/vtk/utility/lagrangelfecache.hh create mode 100644 dune/vtk/utility/lagrangepoints.hh create mode 100644 dune/vtk/utility/test/CMakeLists.txt create mode 100644 dune/vtk/utility/test/test-lagrange.cc create mode 100644 dune/vtk/utility/test/test-lfecache.cc create mode 100644 src/lagrangepoints.cc create mode 100644 src/lagrangereader.cc diff --git a/doc/triangles_2d_order3.vtu b/doc/triangles_2d_order3.vtu new file mode 100644 index 0000000..ff528b4 --- /dev/null +++ b/doc/triangles_2d_order3.vtu @@ -0,0 +1,103 @@ +<VTKFile type="UnstructuredGrid" version="1.0" byte_order="LittleEndian" header_type="UInt64"> + <UnstructuredGrid> + <Piece NumberOfPoints="49" NumberOfCells="8"> + <PointData> + <DataArray type="Float32" Name="DistanceToCenter" format="ascii" RangeMin="0" RangeMax="1.4142135381698608"> + 1.4142135381698608 1 1.4142135381698608 1 0 1 + 1.4142135381698608 1 1.4142135381698608 1.201850414276123 1.054092526435852 1.201850414276123 + 0.9428090453147888 0.7453559637069702 0.6666666269302368 1.054092526435852 0.7453559637069702 0.471404492855072 + 0.3333333134651184 0.6666666269302368 0.3333333134651184 1.054092526435852 1.201850414276123 0.745356023311615 + 0.942808985710144 1.201850414276123 0.4714045226573944 0.7453559637069702 1.054092526435852 0.3333333730697632 + 0.6666666269302368 1.054092526435852 0.745356023311615 0.4714045226573944 0.3333333730697632 1.201850414276123 + 0.942808985710144 0.7453559637069702 0.6666666269302368 1.201850414276123 1.054092526435852 0.4714045822620392 + 0.7453559637069702 1.054092526435852 0.7453559637069702 0.942808985710144 1.201850414276123 1.054092526435852 + 1.201850414276123 + </DataArray> + <DataArray type="Float32" Name="Polynomial" format="ascii" RangeMin="1" RangeMax="49"> + 1 4 15 4 10 26 + 15 26 49 1 1 1 + 1 1 4 1 1 1 + 4 4 4 5 8 5 + 8 16 6 10 18 11 + 15 5 5 6 11 8 + 8 10 15 16 18 14 + 19 29 19 25 36 29 + 36 + </DataArray> + </PointData> + <CellData> + </CellData> + <Points> + <DataArray type="Float32" Name="Points" NumberOfComponents="3" format="ascii" RangeMin="0" RangeMax="2.8284271247461903"> + 0 0 0 1 0 0 + 2 0 0 0 1 0 + 1 1 0 2 1 0 + 0 2 0 1 2 0 + 2 2 0 0.3333333432674408 0 0 + 0.6666666865348816 0 0 0 0.3333333432674408 0 + 0.3333333432674408 0.3333333432674408 0 0.6666666865348816 0.3333333432674408 0 + 1 0.3333333432674408 0 0 0.6666666865348816 0 + 0.3333333432674408 0.6666666865348816 0 0.6666666865348816 0.6666666865348816 0 + 1 0.6666666865348816 0 0.3333333432674408 1 0 + 0.6666666865348816 1 0 1.3333333730697632 0 0 + 1.6666666269302368 0 0 1.3333333730697632 0.3333333432674408 0 + 1.6666666269302368 0.3333333432674408 0 2 0.3333333432674408 0 + 1.3333333730697632 0.6666666865348816 0 1.6666666269302368 0.6666666865348816 0 + 2 0.6666666865348816 0 1.3333333730697632 1 0 + 1.6666666269302368 1 0 0 1.3333333730697632 0 + 0.3333333432674408 1.3333333730697632 0 0.6666666865348816 1.3333333730697632 0 + 1 1.3333333730697632 0 0 1.6666666269302368 0 + 0.3333333432674408 1.6666666269302368 0 0.6666666865348816 1.6666666269302368 0 + 1 1.6666666269302368 0 0.3333333432674408 2 0 + 0.6666666865348816 2 0 1.3333333730697632 1.3333333730697632 0 + 1.6666666269302368 1.3333333730697632 0 2 1.3333333730697632 0 + 1.3333333730697632 1.6666666269302368 0 1.6666666269302368 1.6666666269302368 0 + 2 1.6666666269302368 0 1.3333333730697632 2 0 + 1.6666666269302368 2 0 + <InformationKey name="L2_NORM_FINITE_RANGE" location="vtkDataArray" length="2"> + <Value index="0"> + 0 + </Value> + <Value index="1"> + 2.8284271247 + </Value> + </InformationKey> + <InformationKey name="L2_NORM_RANGE" location="vtkDataArray" length="2"> + <Value index="0"> + 0 + </Value> + <Value index="1"> + 2.8284271247 + </Value> + </InformationKey> + </DataArray> + </Points> + <Cells> + <DataArray type="Int64" Name="connectivity" format="ascii" RangeMin="0" RangeMax="48"> + 0 1 3 9 10 13 + 16 15 11 12 4 3 + 1 20 19 16 13 14 + 18 17 1 2 4 21 + 22 24 26 18 14 23 + 5 4 2 30 29 26 + 24 25 28 27 3 4 + 6 19 20 33 36 35 + 31 32 7 6 4 40 + 39 36 33 34 38 37 + 4 5 7 29 30 42 + 44 38 34 41 8 7 + 5 48 47 44 42 43 + 46 45 + </DataArray> + <DataArray type="Int64" Name="offsets" format="ascii" RangeMin="10" RangeMax="80"> + 10 20 30 40 50 60 + 70 80 + </DataArray> + <DataArray type="UInt8" Name="types" format="ascii" RangeMin="69" RangeMax="69"> + 69 69 69 69 69 69 + 69 69 + </DataArray> + </Cells> + </Piece> + </UnstructuredGrid> +</VTKFile> diff --git a/doc/triangles_3d_order3.vtu b/doc/triangles_3d_order3.vtu new file mode 100644 index 0000000..ec93fc4 --- /dev/null +++ b/doc/triangles_3d_order3.vtu @@ -0,0 +1,140 @@ +<VTKFile type="UnstructuredGrid" version="1.0" byte_order="LittleEndian" header_type="UInt64"> + <UnstructuredGrid> + <Piece NumberOfPoints="49" NumberOfCells="8"> + <PointData Vectors="Result"> + <DataArray type="Float32" Name="DistanceToCenter" format="ascii" RangeMin="0" RangeMax="1.4142135381698608"> + 1.4142135381698608 1 1.4142135381698608 1 0 1 + 1.4142135381698608 1 1.4142135381698608 1.201850414276123 1.054092526435852 1.201850414276123 + 0.9428090453147888 0.7453559637069702 0.6666666269302368 1.054092526435852 0.7453559637069702 0.471404492855072 + 0.3333333134651184 0.6666666269302368 0.3333333134651184 1.054092526435852 1.201850414276123 0.745356023311615 + 0.942808985710144 1.201850414276123 0.4714045226573944 0.7453559637069702 1.054092526435852 0.3333333730697632 + 0.6666666269302368 1.054092526435852 0.745356023311615 0.4714045226573944 0.3333333730697632 1.201850414276123 + 0.942808985710144 0.7453559637069702 0.6666666269302368 1.201850414276123 1.054092526435852 0.4714045822620392 + 0.7453559637069702 1.054092526435852 0.7453559637069702 0.942808985710144 1.201850414276123 1.054092526435852 + 1.201850414276123 + </DataArray> + <DataArray type="Float32" Name="Polynomial" format="ascii" RangeMin="1" RangeMax="49"> + 1 4 15 4 10 26 + 15 26 49 1 1 1 + 1 1 4 1 1 1 + 4 4 4 5 8 5 + 8 16 6 10 18 11 + 15 5 5 6 11 8 + 8 10 15 16 18 14 + 19 29 19 25 36 29 + 36 + </DataArray> + <DataArray type="Float64" Name="Result" NumberOfComponents="3" format="ascii" RangeMin="0.08895213036368632" RangeMax="1.9954079615554763"> + 0 0 1 0 0 1.8414709848078965 + 0 0 1.9092974268256817 0 0 0.5403023058681398 + 0 0 1.3817732906760363 0 0 1.4495997326938215 + 0 0 -0.4161468365471424 0 0 0.4253241482607541 + 0 0 0.4931505902785393 0 0 1.3271947061834561 + 0 0 1.618369818683914 0 0 0.9449569430643503 + 0 0 1.2721516492478064 0 0 1.5633267617482642 + 0 0 1.7864279278722468 0 0 0.7858872484910437 + 0 0 1.1130819546744999 0 0 1.4042570671749577 + 0 0 1.6273582332989402 0 0 0.8674970120515959 + 0 0 1.1586721245520537 0 0 1.9719379107108135 + 0 0 1.9954079615554763 0 0 1.9168948537751636 + 0 0 1.9403649046198266 0 0 1.854254369890032 + 0 0 1.757825159201857 0 0 1.78129521004652 + 0 0 1.6951846753167255 0 0 1.5122402165789532 + 0 0 1.535710267423616 0 0 0.23523753468164693 + 0 0 0.562432240865103 0 0 0.8536073533655608 + 0 0 1.0767085194895434 0 0 -0.09572350846041702 + 0 0 0.23147119772303906 0 0 0.522646310223497 + 0 0 0.7457474763474795 0 0 -0.08895213036368632 + 0 0 0.20222298213677153 0 0 1.2071754453924604 + 0 0 1.2306454962371232 0 0 1.1445349615073286 + 0 0 0.8762144022503964 0 0 0.8996844530950593 + 0 0 0.8135739183652647 0 0 0.5557910741636709 + 0 0 0.5792611250083339 + <InformationKey name="L2_NORM_RANGE" location="vtkDataArray" length="2"> + <Value index="0"> + 0.088952130364 + </Value> + <Value index="1"> + 1.9954079616 + </Value> + </InformationKey> + <InformationKey name="L2_NORM_FINITE_RANGE" location="vtkDataArray" length="2"> + <Value index="0"> + 0.088952130364 + </Value> + <Value index="1"> + 1.9954079616 + </Value> + </InformationKey> + </DataArray> + </PointData> + <CellData> + </CellData> + <Points> + <DataArray type="Float32" Name="Points" NumberOfComponents="3" format="ascii" RangeMin="1" RangeMax="2.871096916901671"> + 0 0 1 1 0 1.8414709568023682 + 2 0 1.9092974662780762 0 1 0.5403022766113281 + 1 1 1.3817732334136963 2 1 1.4495997428894043 + 0 2 -0.416146844625473 1 2 0.42532414197921753 + 2 2 0.49315059185028076 0.3333333432674408 0 1.3271946907043457 + 0.6666666865348816 0 1.6183698177337646 0 0.3333333432674408 0.9449569582939148 + 0.3333333432674408 0.3333333432674408 1.2721517086029053 0.6666666865348816 0.3333333432674408 1.5633267164230347 + 1 0.3333333432674408 1.7864279747009277 0 0.6666666865348816 0.7858872413635254 + 0.3333333432674408 0.6666666865348816 1.113081932067871 0.6666666865348816 0.6666666865348816 1.40425705909729 + 1 0.6666666865348816 1.6273581981658936 0.3333333432674408 1 0.8674970269203186 + 0.6666666865348816 1 1.1586720943450928 1.3333333730697632 0 1.971937894821167 + 1.6666666269302368 0 1.9954079389572144 1.3333333730697632 0.3333333432674408 1.9168949127197266 + 1.6666666269302368 0.3333333432674408 1.940364956855774 2 0.3333333432674408 1.8542543649673462 + 1.3333333730697632 0.6666666865348816 1.7578251361846924 1.6666666269302368 0.6666666865348816 1.7812951803207397 + 2 0.6666666865348816 1.6951847076416016 1.3333333730697632 1 1.5122401714324951 + 1.6666666269302368 1 1.5357102155685425 0 1.3333333730697632 0.23523753881454468 + 0.3333333432674408 1.3333333730697632 0.5624322295188904 0.6666666865348816 1.3333333730697632 0.8536073565483093 + 1 1.3333333730697632 1.0767085552215576 0 1.6666666269302368 -0.09572350978851318 + 0.3333333432674408 1.6666666269302368 0.2314711958169937 0.6666666865348816 1.6666666269302368 0.5226463079452515 + 1 1.6666666269302368 0.745747447013855 0.3333333432674408 2 -0.08895213156938553 + 0.6666666865348816 2 0.20222298800945282 1.3333333730697632 1.3333333730697632 1.2071754932403564 + 1.6666666269302368 1.3333333730697632 1.2306455373764038 2 1.3333333730697632 1.144534945487976 + 1.3333333730697632 1.6666666269302368 0.8762143850326538 1.6666666269302368 1.6666666269302368 0.8996844291687012 + 2 1.6666666269302368 0.8135738968849182 1.3333333730697632 2 0.5557910799980164 + 1.6666666269302368 2 0.5792611241340637 + <InformationKey name="L2_NORM_FINITE_RANGE" location="vtkDataArray" length="2"> + <Value index="0"> + 1 + </Value> + <Value index="1"> + 2.8710969169 + </Value> + </InformationKey> + <InformationKey name="L2_NORM_RANGE" location="vtkDataArray" length="2"> + <Value index="0"> + 1 + </Value> + <Value index="1"> + 2.8710969169 + </Value> + </InformationKey> + </DataArray> + </Points> + <Cells> + <DataArray type="Int64" Name="connectivity" format="ascii" RangeMin="0" RangeMax="48"> + 0 1 3 9 10 13 16 15 11 12 + 4 3 1 20 19 16 13 14 18 17 + 1 2 4 21 22 24 26 18 14 23 + 5 4 2 30 29 26 24 25 28 27 + 3 4 6 19 20 33 36 35 31 32 + 7 6 4 40 39 36 33 34 38 37 + 4 5 7 29 30 42 44 38 34 41 + 8 7 5 48 47 44 42 43 46 45 + </DataArray> + <DataArray type="Int64" Name="offsets" format="ascii" RangeMin="10" RangeMax="80"> + 10 20 30 40 50 60 + 70 80 + </DataArray> + <DataArray type="UInt8" Name="types" format="ascii" RangeMin="69" RangeMax="69"> + 69 69 69 69 69 69 + 69 69 + </DataArray> + </Cells> + </Piece> + </UnstructuredGrid> +</VTKFile> diff --git a/dune.module b/dune.module index e318f46..3ac7b47 100644 --- a/dune.module +++ b/dune.module @@ -8,4 +8,4 @@ Version: 0.2 Maintainer: simon.praetorius@tu-dresden.de #depending on Depends: dune-common (>= 2.6) dune-geometry (>= 2.6) dune-grid dune-uggrid -Suggests: dune-functions dune-spgrid dune-polygongrid dune-alugrid +Suggests: dune-functions dune-spgrid dune-polygongrid dune-alugrid dune-foamgrid diff --git a/dune/vtk/datacollectors/CMakeLists.txt b/dune/vtk/datacollectors/CMakeLists.txt index 3ee908c..0b6e52a 100644 --- a/dune/vtk/datacollectors/CMakeLists.txt +++ b/dune/vtk/datacollectors/CMakeLists.txt @@ -8,3 +8,5 @@ install(FILES unstructureddatacollector.hh yaspdatacollector.hh DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/dune/vtkwriter/datacollectors) + +add_subdirectory(test) \ No newline at end of file diff --git a/dune/vtk/datacollectors/lagrangedatacollector.hh b/dune/vtk/datacollectors/lagrangedatacollector.hh new file mode 100644 index 0000000..d38f341 --- /dev/null +++ b/dune/vtk/datacollectors/lagrangedatacollector.hh @@ -0,0 +1,196 @@ +#pragma once + +#include <cassert> +#include <map> +#include <vector> + +#include <dune/geometry/referenceelements.hh> +#include <dune/grid/common/partitionset.hh> + +#include <dune/vtk/forward.hh> +#include <dune/vtk/vtktypes.hh> +#include <dune/vtk/utility/lagrangepoints.hh> + +#include "unstructureddatacollector.hh" + +namespace Dune { + +/// Implementation of \ref DataCollector for lagrange cells +template <class GridView, int ORDER = -1> +class LagrangeDataCollector + : public UnstructuredDataCollectorInterface<GridView, LagrangeDataCollector<GridView,ORDER>, Partitions::All> +{ + using Self = LagrangeDataCollector; + using Super = UnstructuredDataCollectorInterface<GridView, Self, Partitions::All>; + +public: + static_assert(ORDER != 0, "Order 0 not supported"); + using Super::dim; + using Super::partition; // NOTE: lagrange data-collector currently implemented for the All partition only + +public: + LagrangeDataCollector (GridView const& gridView, int order = (ORDER < 0 ? 2 : ORDER)) + : Super(gridView) + , order_(order) + { + assert(order > 0 && "Order 0 not supported"); + assert(ORDER < 0 || order == ORDER); + } + + /// Construct the point sets + void updateImpl () + { + auto const& indexSet = gridView_.indexSet(); + + pointSets_.clear(); + for (auto gt : indexSet.types(0)) + pointSets_.emplace(gt, order_); + + for (auto& pointSet : pointSets_) + pointSet.second.build(pointSet.first); + + numPoints_ = indexSet.size(dim); + for (auto const& pointSet : pointSets_) { + auto gt = pointSet.first; + auto refElem = referenceElement<double,dim>(gt); + numPoints_ += (pointSet.second.size() - refElem.size(dim)) * indexSet.size(gt); + } + } + + /// Return number of lagrange nodes + std::uint64_t numPointsImpl () const + { + return numPoints_; + } + + /// Return a vector of point coordinates. + /** + * The vector of point coordinates is composed of vertex coordinates first and second + * edge center coordinates. + **/ + template <class T> + std::vector<T> pointsImpl () const + { + std::vector<T> data(this->numPoints() * 3); + auto const& indexSet = gridView_.indexSet(); + + std::size_t shift = indexSet.size(dim); + + for (auto const& element : elements(gridView_, partition)) { + auto geometry = element.geometry(); + auto refElem = referenceElement<T,dim>(element.type()); + + auto const& pointSet = pointSets_.at(element.type()); + unsigned int vertexDOFs = refElem.size(dim); + unsigned int innerDOFs = pointSet.size() - vertexDOFs; + + for (std::size_t i = 0; i < pointSet.size(); ++i) { + auto const& p = pointSet[i]; + if (i < vertexDOFs) + assert(p.localKey().codim() == dim); + + auto const& localKey = p.localKey(); + std::size_t idx = 3 * (localKey.codim() == dim + ? indexSet.subIndex(element, localKey.subEntity(), dim) + : innerDOFs*indexSet.index(element) + (i - vertexDOFs) + shift); + + auto v = geometry.global(p.point()); + for (std::size_t j = 0; j < v.size(); ++j) + data[idx + j] = T(v[j]); + for (std::size_t j = v.size(); j < 3u; ++j) + data[idx + j] = T(0); + } + } + return data; + } + + /// Return number of grid cells + std::uint64_t numCellsImpl () const + { + return gridView_.size(0); + } + + /// \brief Return cell types, offsets, and connectivity. \see Cells + /** + * The cell connectivity is composed of cell vertices first and second cell edges, + * where the indices are grouped [vertex-indices..., (#vertices)+edge-indices...] + **/ + Cells cellsImpl () const + { + Cells cells; + cells.connectivity.reserve(this->numPoints()); + cells.offsets.reserve(this->numCells()); + cells.types.reserve(this->numCells()); + + auto const& indexSet = gridView_.indexSet(); + std::size_t shift = indexSet.size(dim); + + std::int64_t old_o = 0; + for (auto const& element : elements(gridView_, partition)) { + auto refElem = referenceElement<double,dim>(element.type()); + Vtk::CellType cellType(element.type(), Vtk::LAGRANGE); + + auto const& pointSet = pointSets_.at(element.type()); + unsigned int vertexDOFs = refElem.size(dim); + unsigned int innerDOFs = pointSet.size() - vertexDOFs; + + for (std::size_t i = 0; i < pointSet.size(); ++i) { + auto const& p = pointSet[i]; + auto const& localKey = p.localKey(); + std::size_t idx = (localKey.codim() == dim + ? indexSet.subIndex(element, localKey.subEntity(), dim) + : innerDOFs*indexSet.index(element) + (i - vertexDOFs) + shift); + cells.connectivity.push_back(idx); + } + + cells.offsets.push_back(old_o += pointSet.size()); + cells.types.push_back(cellType.type()); + } + return cells; + } + + /// Evaluate the `fct` at element vertices and edge centers in the same order as the point coords. + template <class T, class GlobalFunction> + std::vector<T> pointDataImpl (GlobalFunction const& fct) const + { + int nComps = fct.ncomps(); + std::vector<T> data(this->numPoints() * nComps); + auto const& indexSet = gridView_.indexSet(); + + std::size_t shift = indexSet.size(dim); + + auto localFct = localFunction(fct); + for (auto const& element : elements(gridView_, partition)) { + localFct.bind(element); + auto refElem = referenceElement<T,dim>(element.type()); + + auto const& pointSet = pointSets_.at(element.type()); + unsigned int vertexDOFs = refElem.size(dim); + unsigned int innerDOFs = pointSet.size() - vertexDOFs; + + for (std::size_t i = 0; i < pointSet.size(); ++i) { + auto const& p = pointSet[i]; + auto const& localKey = p.localKey(); + std::size_t idx = nComps * (localKey.codim() == dim + ? indexSet.subIndex(element, localKey.subEntity(), dim) + : innerDOFs*indexSet.index(element) + (i - vertexDOFs) + shift); + + for (int comp = 0; comp < nComps; ++comp) + data[idx + comp] = T(localFct.evaluate(comp, p.point())); + } + localFct.unbind(); + } + return data; + } + +protected: + using Super::gridView_; + + unsigned int order_; + std::uint64_t numPoints_ = 0; + + using PointSet = VtkLagrangePointSet<typename GridView::ctype, GridView::dimension>; + std::map<GeometryType, PointSet> pointSets_; +}; + +} // end namespace Dune diff --git a/dune/vtk/datacollectors/test/CMakeLists.txt b/dune/vtk/datacollectors/test/CMakeLists.txt new file mode 100644 index 0000000..64403b5 --- /dev/null +++ b/dune/vtk/datacollectors/test/CMakeLists.txt @@ -0,0 +1,2 @@ +dune_add_test(SOURCES test-lagrangedatacollector.cc + LINK_LIBRARIES dunevtk) \ No newline at end of file diff --git a/dune/vtk/datacollectors/test/test-lagrangedatacollector.cc b/dune/vtk/datacollectors/test/test-lagrangedatacollector.cc new file mode 100644 index 0000000..99d22c5 --- /dev/null +++ b/dune/vtk/datacollectors/test/test-lagrangedatacollector.cc @@ -0,0 +1,119 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <dune/common/hybridutilities.hh> +#include <dune/common/rangeutilities.hh> +#include <dune/common/test/testsuite.hh> + +#include <dune/grid/yaspgrid.hh> +#if HAVE_UG + #include <dune/grid/uggrid.hh> +#endif + +#include <dune/vtk/datacollectors/continuousdatacollector.hh> +#include <dune/vtk/datacollectors/lagrangedatacollector.hh> + +/** + * This test compares LagrangeDataCollector<1> against the ContinuousDataCollector, thus + * just compares the linear order implementations + **/ + +template <class DataCollector1, class DataCollector2> +void testDataCollector (std::string prefix, Dune::TestSuite& testSuite, DataCollector1& dataCollector1, DataCollector2& dataCollector2) +{ + using std::sqrt; + auto tol = sqrt(std::numeric_limits<double>::epsilon()); + + dataCollector1.update(); + dataCollector2.update(); + + // check that number of points and cells are equal + testSuite.check(dataCollector1.numPoints() == dataCollector2.numPoints(), prefix + "_numPoints"); + testSuite.check(dataCollector1.numCells() == dataCollector2.numCells(), prefix + "_numCells"); + + auto points1 = dataCollector1.template points<double>(); + auto points2 = dataCollector2.template points<double>(); + + // check that point sizes are equal + testSuite.check(points1.size() == points2.size(), prefix + "_points.size"); + testSuite.check(points1.size() == 3*dataCollector1.numPoints(), prefix + "_points.size/3"); + + // check that point coordinates are equal + using std::abs; + for (std::size_t i = 0; i < points1.size(); ++i) + testSuite.check(abs(points1[i] - points2[i]) < tol, prefix + "_points[" + std::to_string(i) + "]"); + + auto cells1 = dataCollector1.cells(); + auto cells2 = dataCollector2.cells(); + + // check that cell sizes are equal + testSuite.check(cells1.types.size() == cells2.types.size(), prefix + "_cells.types.size"); + testSuite.check(cells1.offsets.size() == cells2.offsets.size(), prefix + "_cells.offsets.size"); + testSuite.check(cells1.connectivity.size() == cells2.connectivity.size(), prefix + "_cells.connectivity.size"); + + // NOTE: cells.types do not need to be equal, e.g. LINEAR != LAGRANGE_LINEAR + + // check that offsets are equal + for (std::size_t i = 0; i < cells1.offsets.size(); ++i) + testSuite.check(cells1.offsets[i] == cells2.offsets[i], prefix + "_cells.offsets[" + std::to_string(i) + "]"); + + // check that connectivities are equal + for (std::size_t i = 0; i < cells1.connectivity.size(); ++i) + testSuite.check(cells1.connectivity[i] == cells2.connectivity[i], prefix + "_cells.connectivity[" + std::to_string(i) + "]"); +} + +template <class GridView> +void testGridView (std::string prefix, Dune::TestSuite& testSuite, GridView const& gridView) +{ + // 1. test linear order lagrange data-collector + { + Dune::ContinuousDataCollector<GridView> linearDataCollector(gridView); + + // data collector with template order + Dune::LagrangeDataCollector<GridView, 1> lagrangeDataCollector1a(gridView); + testDataCollector(prefix + "_linear_template", testSuite, lagrangeDataCollector1a, linearDataCollector); + + // data collector with runtime order + Dune::LagrangeDataCollector<GridView> lagrangeDataCollector1b(gridView, 1); + testDataCollector(prefix + "_linear_runtime", testSuite, lagrangeDataCollector1b, linearDataCollector); + } +} + +template <class Grid> +void testGrid (std::string prefix, Dune::TestSuite& testSuite, Grid& grid) +{ + grid.globalRefine(1); + + testGridView(prefix + "_level0", testSuite, grid.levelGridView(0)); + testGridView(prefix + "_leaf", testSuite, grid.leafGridView()); +} + +int main(int argc, char** argv) +{ + using namespace Dune; + MPIHelper::instance(argc, argv); + + TestSuite testSuite; + + YaspGrid<1> yaspGrid1({1.0}, {1}); + YaspGrid<2> yaspGrid2({1.0,1.0}, {1,1}); + YaspGrid<3> yaspGrid3({1.0,1.0,1.0}, {1,1,1}); + + testGrid("yaspgrid_1d", testSuite, yaspGrid1); + testGrid("yaspgrid_2d", testSuite, yaspGrid2); + testGrid("yaspgrid_3d", testSuite, yaspGrid3); + +#if HAVE_UG + auto ugGrid2 = StructuredGridFactory<UGGrid<2>>::createSimplexGrid({0.0,0.0}, {1.0,1.0}, {1u,1u}); + auto ugGrid3 = StructuredGridFactory<UGGrid<3>>::createSimplexGrid({0.0,0.0,0.0}, {1.0,1.0,1.0}, {1u,1u,1u}); + + testGrid("uggrid_2d", testSuite, *ugGrid2); + testGrid("uggrid_3d", testSuite, *ugGrid3); +#endif + + return testSuite.exit(); +} diff --git a/dune/vtk/forward.hh b/dune/vtk/forward.hh index 3b6ac80..f3946ea 100644 --- a/dune/vtk/forward.hh +++ b/dune/vtk/forward.hh @@ -65,6 +65,9 @@ namespace Dune template <class Grid> struct SerialGridCreator; + + template <class Grid> + class LagrangeGridCreator; // @} gridcreators diff --git a/dune/vtk/gridcreatorinterface.hh b/dune/vtk/gridcreatorinterface.hh index c8c8fb4..02def76 100644 --- a/dune/vtk/gridcreatorinterface.hh +++ b/dune/vtk/gridcreatorinterface.hh @@ -17,15 +17,17 @@ namespace Dune /** * Construct a grid from data read from VTK files. * - * \tparam GridView Model of Dune::GridView - * \tparam Derived Implementation of a concrete GridCreator. + * \tparam GridType Model of Dune::Grid + * \tparam GlobalCoordType Type of the global coordinates. + * \tparam DerivedType Implementation of a concrete GridCreator. **/ - template <class G, class Derived> + template <class GridType, class DerivedType> class GridCreatorInterface { public: - using Grid = G; + using Grid = GridType; using GlobalCoordinate = typename Grid::template Codim<0>::Entity::Geometry::GlobalCoordinate; + using Derived = DerivedType; public: /// Constructor. Stores a reference to the passed GridFactory @@ -60,6 +62,12 @@ namespace Dune return *factory_; } + /// Return the associated (const) GridFactory + GridFactory<Grid> const& factory () const + { + return *factory_; + } + /// Return the mpi collective communicator auto comm () const { diff --git a/dune/vtk/gridcreators/CMakeLists.txt b/dune/vtk/gridcreators/CMakeLists.txt index 299b4d3..b605ec6 100644 --- a/dune/vtk/gridcreators/CMakeLists.txt +++ b/dune/vtk/gridcreators/CMakeLists.txt @@ -4,6 +4,7 @@ install(FILES continuousgridcreator.hh derivedgridcreator.hh discontinuousgridcreator.hh + lagrangegridcreator.hh parallelgridcreator.hh serialgridcreator.hh DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/dune/vtkwriter/gridcreators) diff --git a/dune/vtk/gridcreators/continuousgridcreator.hh b/dune/vtk/gridcreators/continuousgridcreator.hh index 03270a2..3db1d0c 100644 --- a/dune/vtk/gridcreators/continuousgridcreator.hh +++ b/dune/vtk/gridcreators/continuousgridcreator.hh @@ -20,7 +20,8 @@ namespace Dune struct ContinuousGridCreator : public GridCreatorInterface<Grid, ContinuousGridCreator<Grid>> { - using Super = GridCreatorInterface<Grid, ContinuousGridCreator<Grid>>; + using Self = ContinuousGridCreator; + using Super = GridCreatorInterface<Grid, ContinuousGridCreator>; using GlobalCoordinate = typename Super::GlobalCoordinate; ContinuousGridCreator (GridFactory<Grid>& factory) diff --git a/dune/vtk/gridcreators/discontinuousgridcreator.hh b/dune/vtk/gridcreators/discontinuousgridcreator.hh index 5160b00..c285ba5 100644 --- a/dune/vtk/gridcreators/discontinuousgridcreator.hh +++ b/dune/vtk/gridcreators/discontinuousgridcreator.hh @@ -19,7 +19,8 @@ namespace Dune struct DiscontinuousGridCreator : public GridCreatorInterface<Grid, DiscontinuousGridCreator<Grid>> { - using Super = GridCreatorInterface<Grid, DiscontinuousGridCreator<Grid>>; + using Self = DiscontinuousGridCreator; + using Super = GridCreatorInterface<Grid, DiscontinuousGridCreator>; using GlobalCoordinate = typename Super::GlobalCoordinate; struct CoordLess diff --git a/dune/vtk/gridcreators/lagrangegridcreator.hh b/dune/vtk/gridcreators/lagrangegridcreator.hh new file mode 100644 index 0000000..855b7c4 --- /dev/null +++ b/dune/vtk/gridcreators/lagrangegridcreator.hh @@ -0,0 +1,344 @@ +#pragma once + +#include <cassert> +#include <cstdint> +#include <limits> +#include <vector> + +#include <dune/common/exceptions.hh> +#include <dune/common/hybridutilities.hh> +#include <dune/common/std/optional.hh> +#include <dune/geometry/utility/typefromvertexcount.hh> +#include <dune/localfunctions/lagrange.hh> +#include <dune/grid/common/gridfactory.hh> + +#include <dune/vtk/forward.hh> +#include <dune/vtk/vtktypes.hh> +#include <dune/vtk/gridcreatorinterface.hh> +#include <dune/vtk/utility/lagrangepoints.hh> + +namespace Dune +{ + // \brief Create a grid from data that represents higher (lagrange) cells. + /** + * The grid is created from the first nodes of a cell parametrization, representing + * the corner vertices. Thus a piecewise "flat" grid is constructed. The parametrization + * is 1. passed as a local element parametrization to the `insertElement()` function of a + * gridFactory to allow the grid itself to handle the parametrization and 2. is stored + * internally that can be accessed by using this GridCreator object as a grid function, + * or by extracting locally the parametrization on each existing grid element after + * creation of the grid. + * + * So, the LagrangeGridCreator models both, a `GridCreator` and a `GridFunction`. + **/ + template <class Grid> + struct LagrangeGridCreator + : public GridCreatorInterface<Grid, LagrangeGridCreator<Grid>> + { + using Self = LagrangeGridCreator; + using Super = GridCreatorInterface<Grid, Self>; + using GlobalCoordinate = typename Super::GlobalCoordinate; + + using Nodes = std::vector<GlobalCoordinate>; + + struct ElementParametrization + { + GeometryType type; //< Geometry type of the element + std::vector<std::int64_t> nodes; //< Indices of the w.r.t. `nodes_` vector + std::vector<unsigned int> corners; //< Insertion-indices of the element corner nodes + }; + + using Parametrization = std::vector<ElementParametrization>; + using Element = typename Grid::template Codim<0>::Entity; + using LocalCoordinate = typename Element::Geometry::LocalCoordinate; + + class LocalParametrization; + class LocalFunction; + + public: + using Super::factory; + + LagrangeGridCreator (GridFactory<Grid>& factory) + : Super(factory) + {} + + /// Implementation of the interface function `insertVertices()` + void insertVerticesImpl (std::vector<GlobalCoordinate> const& points, + std::vector<std::uint64_t> const& /*point_ids*/) + { + // store point coordinates in member variable + nodes_ = points; + } + + template <class F> + using HasParametrizedElements = decltype(std::declval<F>().insertElement(std::declval<GeometryType>(), + std::declval<std::vector<unsigned int> const&>(), std::declval<std::function<GlobalCoordinate(LocalCoordinate)>>())); + + /// Implementation of the interface function `insertElements()` + void insertElementsImpl (std::vector<std::uint8_t> const& types, + std::vector<std::int64_t> const& offsets, + std::vector<std::int64_t> const& connectivity) + { + assert(nodes_.size() > 0); + + // mapping of node index to element-vertex index + std::vector<std::int64_t> elementVertices(nodes_.size(), -1); + parametrization_.resize(types.size()); + + std::int64_t vertexIndex = 0; + for (std::size_t i = 0; i < types.size(); ++i) { + auto type = Vtk::to_geometry(types[i]); + Vtk::CellType cellType{type}; + auto refElem = referenceElement<double,Grid::dimension>(type); + + std::int64_t shift = (i == 0 ? 0 : offsets[i-1]); + int nNodes = offsets[i] - shift; + int nVertices = refElem.size(Grid::dimension); + + // insert vertices into grid and construct element vertices + std::vector<unsigned int> element(nVertices); + for (int j = 0; j < nVertices; ++j) { + auto index = connectivity.at(shift + j); + auto& vertex = elementVertices.at(index); + if (vertex < 0) { + factory().insertVertex(nodes_.at(index)); + vertex = vertexIndex++; + } + element[j] = vertex; + } + + // permute element indices + if (!cellType.noPermutation()) { + // apply index permutation + std::vector<unsigned int> cell(element.size()); + for (int j = 0; j < element.size(); ++j) + cell[j] = element[cellType.permutation(j)]; + std::swap(element, cell); + } + + // fill vector of element parametrizations + parametrization_[i].type = type; + parametrization_[i].nodes.resize(nNodes); + for (int j = 0; j < nNodes; ++j) + parametrization_[i].nodes[j] = connectivity.at(shift + j); + parametrization_[i].corners = element; + + // try to create element with parametrization + if constexpr (Std::is_detected_v<HasParametrizedElements, GridFactory<Grid>>) { + try { + factory().insertElement(type, element, localParametrization(i)); + } catch (Dune::GridError const& /* notImplemented */) { + factory().insertElement(type, element); + } + } else { + factory().insertElement(type, element); + } + } + } + + /// \brief Construct an element parametrization + /** + * The returned LocalParametrization is a mapping `GlobalCoordinate(LocalCoordinate)` + * where `LocalCoordinate is w.r.t. the local coordinate system in an element with + * given `insertionIndex` (defined by the inserted corner vertices) and + * `GlobalCoordinate` a world coordinate in the parametrized grid. + **/ + LocalParametrization localParametrization (unsigned int insertionIndex) const + { + assert(!nodes_.empty() && !parametrization_.empty()); + auto const& localParam = parametrization_.at(insertionIndex); + return LocalParametrization{nodes_, localParam, order(localParam)}; + } + + /// \brief Construct an element parametrization + /** + * The returned LocalParametrization is a mapping `GlobalCoordinate(LocalCoordinate)` + * where `LocalCoordinate is w.r.t. the local coordinate system in the passed element + * and `GlobalCoordinate` a world coordinate in the parametrized grid. + * + * Note, when an element is passed, it might have a different local coordinate system + * than the coordinate system used to defined the element parametrization. Thus coordinate + * transform is internally chained to the evaluation of the local parametrization. This + * local geometry transform is obtained by figuring out the permutation of corners in the + * element corresponding to the inserted corner vertices. + **/ + LocalParametrization localParametrization (Element const& element) const + { + assert(!nodes_.empty() && !parametrization_.empty()); + + unsigned int insertionIndex = factory().insertionIndex(element); + auto const& localParam = parametrization_.at(insertionIndex); + assert(element.type() == localParam.type); + + // collect indices of vertices + std::vector<unsigned int> indices(element.subEntities(Grid::dimension)); + for (unsigned int i = 0; i < element.subEntities(Grid::dimension); ++i) + indices[i] = factory().insertionIndex(element.template subEntity<Grid::dimension>(i)); + + // calculate permutation vector + std::vector<unsigned int> permutation(indices.size()); + for (std::size_t i = 0; i < indices.size(); ++i) { + auto it = std::find(localParam.corners.begin(), localParam.corners.end(), indices[i]); + assert(it != localParam.corners.end()); + permutation[i] = std::distance(localParam.corners.begin(), it); + } + + return LocalParametrization{nodes_, localParam, order(localParam), permutation}; + } + + /// \brief Local function representing the parametrization of the grid. + /** + * The returned LocalFunction object models a Functions::Concept::LocalFunction + * and can thus be bound to an element of the created grid and evaluated in the + * local coordinates of the bound element. + * + * It is implemented in terms of the \ref LocalParametrization function returned by + * the method \ref localParametrization(element). See comments there for furhter + * details. + * + * Note, this methods requires the GridCreator to be bassed by lvalue-reference. This + * is necessary, since we want to guarantee that all internal storage is preserved while + * evaluating the local function. + **/ + friend LocalFunction localFunction (LagrangeGridCreator& gridCreator) + { + return LocalFunction{gridCreator}; + } + + /// Determine lagrange order from number of points + template <class LocalParam> + unsigned int order (LocalParam const& localParam) const + { + GeometryType type = localParam.type; + std::size_t nNodes = localParam.nodes.size(); + for (unsigned int o = 1; o <= nNodes; ++o) + if (numLagrangePoints(type.id(), type.dim(), o) == nNodes) + return o; + + return 1; + } + + /// Determine lagrange order from number of points from the first element parametrization + unsigned int order () const + { + assert(!parametrization_.empty()); + return order(parametrization_.front()); + } + + private: + /// All point coordinates inclusing the higher-order lagrange points + Nodes nodes_; + + /// Parametrization for all elements + Parametrization parametrization_; + }; + + + template <class Grid> + class LagrangeGridCreator<Grid>::LocalParametrization + { + using ctype = typename Grid::ctype; + + using GlobalCoordinate = typename Grid::template Codim<0>::Entity::Geometry::GlobalCoordinate; + using LocalCoordinate = typename Grid::template Codim<0>::Entity::Geometry::LocalCoordinate; + using LocalGeometry = MultiLinearGeometry<ctype,Grid::dimension,Grid::dimension>; + + using LocalFE = LagrangeLocalFiniteElement<VtkLagrangePointSet, Grid::dimension, ctype, ctype>; + using LocalBasis = typename LocalFE::Traits::LocalBasisType; + using LocalBasisTraits = typename LocalBasis::Traits; + + public: + /// Construct a local element parametrization + template <class Nodes, class LocalParam> + LocalParametrization (Nodes const& nodes, LocalParam const& param, unsigned int order) + : localFE_(param.type, order) + , localNodes_(param.nodes.size()) + { + for (std::size_t i = 0; i < localNodes_.size(); ++i) + localNodes_[i] = nodes[param.nodes[i]]; + } + + /// Construct a local element parametrization for elements with permuted corners + template <class Nodes, class LocalParam, class Permutation> + LocalParametrization (Nodes const& nodes, LocalParam const& param, unsigned int order, Permutation const& permutation) + : LocalParametrization(nodes, param, order) + { + auto refElem = referenceElement<ctype,Grid::dimension>(param.type); + std::vector<LocalCoordinate> corners(permutation.size()); + for (std::size_t i = 0; i < permutation.size(); ++i) + corners[i] = refElem.position(permutation[i], Grid::dimension); + + localGeometry_.emplace(param.type, corners); + } + + /// Evaluate the local parametrization in local coordinates + template <class LocalCoordinate> + GlobalCoordinate operator() (LocalCoordinate const& local) const + { + // map coordinates if element corners are permuted + LocalCoordinate x = localGeometry_ ? localGeometry_->global(local) : local; + + LocalBasis const& localBasis = localFE_.localBasis(); + localBasis.evaluateFunction(x, shapeValues_); + assert(shapeValues_.size() == localNodes_.size()); + + GlobalCoordinate out(0); + for (std::size_t i = 0; i < shapeValues_.size(); ++i) + out.axpy(shapeValues_[i], localNodes_[i]); + + return out; + } + + private: + LocalFE localFE_; + std::vector<GlobalCoordinate> localNodes_; + Std::optional<LocalGeometry> localGeometry_; + + mutable std::vector<typename LocalBasisTraits::RangeType> shapeValues_; + }; + + + template <class Grid> + class LagrangeGridCreator<Grid>::LocalFunction + { + using ctype = typename Grid::ctype; + using LocalContext = typename Grid::template Codim<0>::Entity; + using GlobalCoordinate = typename LocalContext::Geometry::GlobalCoordinate; + using LocalCoordinate = typename LocalContext::Geometry::LocalCoordinate; + using LocalParametrization = typename LagrangeGridCreator::LocalParametrization; + + public: + explicit LocalFunction (LagrangeGridCreator& gridCreator) + : gridCreator_(&gridCreator) + {} + + /// Collect a local parametrization on the element + void bind (LocalContext const& element) + { + localContext_ = element; + localParametrization_.emplace(gridCreator_->localParametrization(element)); + } + + void unbind () { /* do nothing */ } + + /// Evaluate the local parametrization in local coordinates + GlobalCoordinate operator() (LocalCoordinate const& local) const + { + assert(!!localParametrization_); + return (*localParametrization_)(local); + } + + /// Return the bound element + LocalContext const& localContext () const + { + return localContext_; + } + + private: + LagrangeGridCreator const* gridCreator_; + + LocalContext localContext_; + Std::optional<LocalParametrization> localParametrization_; + }; + +} // end namespace Dune diff --git a/dune/vtk/utility/CMakeLists.txt b/dune/vtk/utility/CMakeLists.txt index e376849..cdc7dce 100644 --- a/dune/vtk/utility/CMakeLists.txt +++ b/dune/vtk/utility/CMakeLists.txt @@ -5,6 +5,10 @@ dune_add_library("filesystem" OBJECT install(FILES enum.hh filesystem.hh + lagrangelfecache.hh + lagrangepoints.hh string.hh uid.hh DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/dune/vtkwriter/utility) + + add_subdirectory(test) \ No newline at end of file diff --git a/dune/vtk/utility/lagrangelfecache.hh b/dune/vtk/utility/lagrangelfecache.hh new file mode 100644 index 0000000..ac2db68 --- /dev/null +++ b/dune/vtk/utility/lagrangelfecache.hh @@ -0,0 +1,35 @@ +#pragma once + +#include <map> + +#include <dune/geometry/type.hh> +#include <dune/localfunctions/lagrange.hh> +#include <dune/vtk/utility/lagrangepoints.hh> + +namespace Dune +{ + // LocalFiniteElement for the lagrange pointset compatible with VTK + template <class Domain, class Range, int dim, int ORDER=-1> + class LagrangeLFECache + { + public: + using FiniteElementType = LagrangeLocalFiniteElement<VtkLagrangePointSet, dim, Domain, Range>; + + LagrangeLFECache (unsigned int order = ORDER) + : order_(order) + {} + + FiniteElementType const& get (GeometryType type) + { + auto it = data_.find(type); + if (it == data_.end()) + it = data_.emplace(type,FiniteElementType(type,order_)).first; + return it->second; + } + + private: + unsigned int order_; + std::map<GeometryType, FiniteElementType> data_; + }; + +} // end namespace Dune diff --git a/dune/vtk/utility/lagrangepoints.hh b/dune/vtk/utility/lagrangepoints.hh new file mode 100644 index 0000000..b598c89 --- /dev/null +++ b/dune/vtk/utility/lagrangepoints.hh @@ -0,0 +1,605 @@ +#pragma once + +#include <cassert> +#include <array> + +#include <dune/common/exceptions.hh> +#include <dune/geometry/type.hh> +#include <dune/localfunctions/lagrange/equidistantpoints.hh> + +namespace Dune { + +namespace Impl { + // forward declaration + template <class K, unsigned int dim> + class VtkLagrangePointSetBuilder; +} + + +/// \brief A set of lagrange points compatible with the numbering of VTK +/** + * \tparam K Field-type for the coordinates + * \tparam dim Dimension of the coordinates + **/ +template <class K, unsigned int dim> +class VtkLagrangePointSet + : public EmptyPointSet<K, dim> +{ + using Super = EmptyPointSet<K, dim>; + +public: + static const unsigned int dimension = dim; + + VtkLagrangePointSet (std::size_t order) + : Super(order) + { + assert(order > 0); + } + + /// Fill the lagrange points for the given geometry type + void build (GeometryType gt) + { + assert(gt.dim() == dimension); + builder_(gt, order(), points_); + } + + /// Fill the lagrange points for the given topology type `Topology` + template <class Topology> + bool build () + { + build(GeometryType(Topology{})); + return true; + } + + /// Returns whether the point set support the given topology type `Topology` and can + /// generate point for the given order. + template <class Topology> + static bool supports (std::size_t order) + { + return true; + } + + using Super::order; + +private: + using Super::points_; + Impl::VtkLagrangePointSetBuilder<K,dim> builder_; +}; + + +namespace Impl { + +// Build for lagrange point sets in different dimensions +// Specialized for dim=1,2,3 +template <class K, unsigned int dim> +class VtkLagrangePointSetBuilder +{ +public: + template <class Points> + void operator()(GeometryType, unsigned int, Points& points) const + { + DUNE_THROW(Dune::NotImplemented, + "Lagrange points not yet implemented for this GeometryType."); + } +}; + +/** + * The implementation of the point set builder is directly derived from VTK. + * Modification are a change in data-types and naming scheme. Additionally + * a LocalKey is created to reflect the concept of a Dune PointSet. + * + * Included is the license of the BSD 3-clause License included in the VTK + * source code from 2020/04/13 in commit b90dad558ce28f6d321420e4a6b17e23f5227a1c + * of git repository https://gitlab.kitware.com/vtk/vtk. + * + Program: Visualization Toolkit + Module: Copyright.txt + + Copyright (c) 1993-2015 Ken Martin, Will Schroeder, Bill Lorensen + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither name of Ken Martin, Will Schroeder, or Bill Lorensen nor the names + of any contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + 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. + * + **/ + +// Lagrange points on point geometries +template <class K> +class VtkLagrangePointSetBuilder<K,0> +{ + static constexpr int dim = 0; + using LP = LagrangePoint<K,dim>; + using Vec = typename LP::Vector; + using Key = LocalKey; + +public: + template <class Points> + void operator()(GeometryType gt, int /*order*/, Points& points) const + { + assert(gt.isVertex()); + points.push_back(LP{Vec{},Key{0,0,0}}); + } +}; + + +// Lagrange points on line geometries +template <class K> +class VtkLagrangePointSetBuilder<K,1> +{ + static constexpr int dim = 1; + using LP = LagrangePoint<K,dim>; + using Vec = typename LP::Vector; + using Key = LocalKey; + +public: + template <class Points> + void operator()(GeometryType gt, int order, Points& points) const + { + assert(gt.isLine()); + + // Vertex nodes + points.push_back(LP{Vec{0.0}, Key{0,dim,0}}); + points.push_back(LP{Vec{1.0}, Key{1,dim,0}}); + + if (order > 1) { + // Inner nodes + Vec p{0.0}; + for (unsigned int i = 0; i < order-1; i++) + { + p[0] += 1.0 / order; + points.push_back(LP{p,Key{0,dim-1,i}}); + } + } + } +}; + + +// Lagrange points on 2d geometries +template <class K> +class VtkLagrangePointSetBuilder<K,2> +{ + static constexpr int dim = 2; + using LP = LagrangePoint<K,dim>; + using Vec = typename LP::Vector; + using Key = LocalKey; + + friend class VtkLagrangePointSetBuilder<K,3>; + +public: + template <class Points> + void operator()(GeometryType gt, int order, Points& points) const + { + std::size_t nPoints = numLagrangePoints(gt.id(), dim, order); + + if (gt.isTriangle()) + buildTriangle(nPoints, order, points); + else if (gt.isQuadrilateral()) + buildQuad(nPoints, order, points); + else { + DUNE_THROW(Dune::NotImplemented, + "Lagrange points not yet implemented for this GeometryType."); + } + + assert(points.size() == nPoints); + } + +private: + // Construct the point set in a triangle element. + // Loop from the outside to the inside + template <class Points> + void buildTriangle (std::size_t nPoints, int order, Points& points) const + { + points.reserve(nPoints); + + const int nVertexDOFs = 3; + const int nEdgeDOFs = 3 * (order-1); + + static const unsigned int vertexPerm[3] = {0,1,2}; + static const unsigned int edgePerm[3] = {0,2,1}; + + auto calcKey = [&](int p) -> Key + { + if (p < nVertexDOFs) { + return Key{vertexPerm[p], dim, 0}; + } + else if (p < nVertexDOFs+nEdgeDOFs) { + unsigned int entityIndex = (p - nVertexDOFs) / (order-1); + unsigned int index = (p - nVertexDOFs) % (order-1); + return Key{edgePerm[entityIndex], dim-1, index}; + } + else { + unsigned int index = p - (nVertexDOFs + nEdgeDOFs); + return Key{0, dim-2, index}; + } + }; + + std::array<int,3> bindex; + + double order_d = double(order); + for (std::size_t p = 0; p < nPoints; ++p) { + barycentricIndex(p, bindex, order); + Vec point{bindex[0] / order_d, bindex[1] / order_d}; + points.push_back(LP{point, calcKey(p)}); + } + } + + // "Barycentric index" is a triplet of integers, each running from 0 to + // <Order>. It is the index of a point on the triangle in barycentric + // coordinates. + static void barycentricIndex (int index, std::array<int,3>& bindex, int order) + { + int max = order; + int min = 0; + + // scope into the correct triangle + while (index != 0 && index >= 3 * order) + { + index -= 3 * order; + max -= 2; + min++; + order -= 3; + } + + // vertex DOFs + if (index < 3) + { + bindex[index] = bindex[(index + 1) % 3] = min; + bindex[(index + 2) % 3] = max; + } + // edge DOFs + else + { + index -= 3; + int dim = index / (order - 1); + int offset = (index - dim * (order - 1)); + bindex[(dim + 1) % 3] = min; + bindex[(dim + 2) % 3] = (max - 1) - offset; + bindex[dim] = (min + 1) + offset; + } + } + + + // Construct the point set in the quad element + // 1. build equispaced points with index tuple (i,j) + // 2. map index tuple to DOF index and LocalKey + template <class Points> + void buildQuad(std::size_t nPoints, int order, Points& points) const + { + points.resize(nPoints); + + std::array<int,2> orders{order, order}; + std::array<Vec,4> nodes{Vec{0., 0.}, Vec{1., 0.}, Vec{1., 1.}, Vec{0., 1.}}; + + for (int n = 0; n <= orders[1]; ++n) { + for (int m = 0; m <= orders[0]; ++m) { + // int idx = pointIndexFromIJ(m,n,orders); + + const double r = double(m) / orders[0]; + const double s = double(n) / orders[1]; + Vec p = (1.0 - r) * (nodes[3] * s + nodes[0] * (1.0 - s)) + + r * (nodes[2] * s + nodes[1] * (1.0 - s)); + + auto [idx,key] = calcQuadKey(m,n,orders); + points[idx] = LP{p, key}; + // points[idx] = LP{p, calcQuadKey(n,m,orders)}; + } + } + } + + // Obtain the VTK DOF index of the node (i,j) in the quad element + // and construct a LocalKey + static std::pair<int,Key> calcQuadKey (int i, int j, std::array<int,2> order) + { + const bool ibdy = (i == 0 || i == order[0]); + const bool jbdy = (j == 0 || j == order[1]); + const int nbdy = (ibdy ? 1 : 0) + (jbdy ? 1 : 0); // How many boundaries do we lie on at once? + + int dof = 0; + unsigned int entityIndex = 0; + unsigned int index = 0; + + if (nbdy == 2) // Vertex DOF + { + dof = (i ? (j ? 2 : 1) : (j ? 3 : 0)); + entityIndex = (j ? (i ? 3 : 2) : (i ? 1 : 0)); + return std::make_pair(dof,Key{entityIndex, dim, 0}); + } + + int offset = 4; + if (nbdy == 1) // Edge DOF + { + if (!ibdy) { + dof = (i - 1) + (j ? order[0]-1 + order[1]-1 : 0) + offset; + entityIndex = j ? 3 : 2; + index = i-1; + } + else if (!jbdy) { + dof = (j - 1) + (i ? order[0]-1 : 2 * (order[0]-1) + order[1]-1) + offset; + entityIndex = i ? 1 : 0; + index = j-1; + } + return std::make_pair(dof, Key{entityIndex, dim-1, index}); + } + + offset += 2 * (order[0]-1 + order[1]-1); + + // nbdy == 0: Face DOF + dof = offset + (i - 1) + (order[0]-1) * ((j - 1)); + Key innerKey = VtkLagrangePointSetBuilder<K,2>::calcQuadKey(i-1,j-1,{order[0]-2, order[1]-2}).second; + return std::make_pair(dof, Key{0, dim-2, innerKey.index()}); + } +}; + + +// Lagrange points on 3d geometries +template <class K> +class VtkLagrangePointSetBuilder<K,3> +{ + static constexpr int dim = 3; + using LP = LagrangePoint<K,dim>; + using Vec = typename LP::Vector; + using Key = LocalKey; + +public: + template <class Points> + void operator() (GeometryType gt, unsigned int order, Points& points) const + { + std::size_t nPoints = numLagrangePoints(gt.id(), dim, order); + + if (gt.isTetrahedron()) + buildTetra(nPoints, order, points); + else if (gt.isHexahedron()) + buildHex(nPoints, order, points); + else { + DUNE_THROW(Dune::NotImplemented, + "Lagrange points not yet implemented for this GeometryType."); + } + + assert(points.size() == nPoints); + } + +private: + // Construct the point set in the tetrahedron element + // 1. construct barycentric (index) coordinates + // 2. obtains the DOF index, LocalKey and actual coordinate from barycentric index + template <class Points> + void buildTetra (std::size_t nPoints, int order, Points& points) const + { + points.reserve(nPoints); + + const int nVertexDOFs = 4; + const int nEdgeDOFs = 6 * (order-1); + const int nFaceDOFs = 4 * (order-1)*(order-2)/2; + + static const unsigned int vertexPerm[4] = {0,1,2,3}; + static const unsigned int edgePerm[6] = {0,2,1,3,4,5}; + static const unsigned int facePerm[4] = {1,2,0,3}; + + auto calcKey = [&](int p) -> Key + { + if (p < nVertexDOFs) { + return Key{vertexPerm[p], dim, 0}; + } + else if (p < nVertexDOFs+nEdgeDOFs) { + unsigned int entityIndex = (p - nVertexDOFs) / (order-1); + unsigned int index = (p - nVertexDOFs) % (order-1); + return Key{edgePerm[entityIndex], dim-1, index}; + } + else if (p < nVertexDOFs+nEdgeDOFs+nFaceDOFs) { + unsigned int index = (p - (nVertexDOFs + nEdgeDOFs)) % ((order-1)*(order-2)/2); + unsigned int entityIndex = (p - (nVertexDOFs + nEdgeDOFs)) / ((order-1)*(order-2)/2); + return Key{facePerm[entityIndex], dim-2, index}; + } + else { + unsigned int index = p - (nVertexDOFs + nEdgeDOFs + nFaceDOFs); + return Key{0, dim-3, index}; + } + }; + + std::array<int,4> bindex; + + double order_d = double(order); + for (std::size_t p = 0; p < nPoints; ++p) { + barycentricIndex(p, bindex, order); + Vec point{bindex[0] / order_d, bindex[1] / order_d, bindex[2] / order_d}; + points.push_back(LP{point, calcKey(p)}); + } + } + + // "Barycentric index" is a set of 4 integers, each running from 0 to + // <Order>. It is the index of a point in the tetrahedron in barycentric + // coordinates. + static void barycentricIndex (std::size_t p, std::array<int,4>& bindex, int order) + { + const int nVertexDOFs = 4; + const int nEdgeDOFs = 6 * (order-1); + + static const int edgeVertices[6][2] = {{0,1}, {1,2}, {2,0}, {0,3}, {1,3}, {2,3}}; + static const int linearVertices[4][4] = {{0,0,0,1}, {1,0,0,0}, {0,1,0,0}, {0,0,1,0}}; + static const int vertexMaxCoords[4] = {3,0,1,2}; + static const int faceBCoords[4][3] = {{0,2,3}, {2,0,1}, {2,1,3}, {1,0,3}}; + static const int faceMinCoord[4] = {1,3,0,2}; + + int max = order; + int min = 0; + + // scope into the correct tetra + while (p >= 2 * (order * order + 1) && p != 0 && order > 3) + { + p -= 2 * (order * order + 1); + max -= 3; + min++; + order -= 4; + } + + // vertex DOFs + if (p < nVertexDOFs) + { + for (int coord = 0; coord < 4; ++coord) + bindex[coord] = (coord == vertexMaxCoords[p] ? max : min); + } + // edge DOFs + else if (p < nVertexDOFs+nEdgeDOFs) + { + int edgeId = (p - nVertexDOFs) / (order-1); + int vertexId = (p - nVertexDOFs) % (order-1); + for (int coord = 0; coord < 4; ++coord) + { + bindex[coord] = min + + (linearVertices[edgeVertices[edgeId][0]][coord] * (max - min - 1 - vertexId) + + linearVertices[edgeVertices[edgeId][1]][coord] * (1 + vertexId)); + } + } + // face DOFs + else + { + int faceId = (p - (nVertexDOFs+nEdgeDOFs)) / ((order-2)*(order-1)/2); + int vertexId = (p - (nVertexDOFs+nEdgeDOFs)) % ((order-2)*(order-1)/2); + + std::array<int,3> projectedBIndex; + if (order == 3) + projectedBIndex[0] = projectedBIndex[1] = projectedBIndex[2] = 0; + else + VtkLagrangePointSetBuilder<K,2>::barycentricIndex(vertexId, projectedBIndex, order-3); + + for (int i = 0; i < 3; i++) + bindex[faceBCoords[faceId][i]] = (min + 1 + projectedBIndex[i]); + + bindex[faceMinCoord[faceId]] = min; + } + } + +private: + // Construct the point set in the heyhedral element + // 1. build equispaced points with index tuple (i,j,k) + // 2. map index tuple to DOF index and LocalKey + template <class Points> + void buildHex (std::size_t nPoints, int order, Points& points) const + { + points.resize(nPoints); + + std::array<int,3> orders{order, order, order}; + std::array<Vec,8> nodes{Vec{0., 0., 0.}, Vec{1., 0., 0.}, Vec{1., 1., 0.}, Vec{0., 1., 0.}, + Vec{0., 0., 1.}, Vec{1., 0., 1.}, Vec{1., 1., 1.}, Vec{0., 1., 1.}}; + + for (int p = 0; p <= orders[2]; ++p) { + for (int n = 0; n <= orders[1]; ++n) { + for (int m = 0; m <= orders[0]; ++m) { + const double r = double(m) / orders[0]; + const double s = double(n) / orders[1]; + const double t = double(p) / orders[2]; + Vec point = (1.0-r) * ((nodes[3] * (1.0-t) + nodes[7] * t) * s + (nodes[0] * (1.0-t) + nodes[4] * t) * (1.0-s)) + + r * ((nodes[2] * (1.0-t) + nodes[6] * t) * s + (nodes[1] * (1.0-t) + nodes[5] * t) * (1.0-s)); + + auto [idx,key] = calcHexKey(m,n,p,orders); + points[idx] = LP{point, key}; + } + } + } + } + + // Obtain the VTK DOF index of the node (i,j,k) in the hexahedral element + static std::pair<int,Key> calcHexKey (int i, int j, int k, std::array<int,3> order) + { + const bool ibdy = (i == 0 || i == order[0]); + const bool jbdy = (j == 0 || j == order[1]); + const bool kbdy = (k == 0 || k == order[2]); + const int nbdy = (ibdy ? 1 : 0) + (jbdy ? 1 : 0) + (kbdy ? 1 : 0); // How many boundaries do we lie on at once? + + int dof = 0; + unsigned int entityIndex = 0; + unsigned int index = 0; + + if (nbdy == 3) // Vertex DOF + { + dof = (i ? (j ? 2 : 1) : (j ? 3 : 0)) + (k ? 4 : 0); + entityIndex = (i ? 1 : 0) + (j ? 2 : 0) + (k ? 4 : 0); + return std::make_pair(dof, Key{entityIndex, dim, 0}); + } + + int offset = 8; + if (nbdy == 2) // Edge DOF + { + entityIndex = (k ? 8 : 4); + if (!ibdy) + { // On i axis + dof = (i - 1) + (j ? order[0]-1 + order[1]-1 : 0) + (k ? 2 * (order[0]-1 + order[1]-1) : 0) + offset; + index = i; + entityIndex += (i ? 1 : 0); + } + else if (!jbdy) + { // On j axis + dof = (j - 1) + (i ? order[0]-1 : 2 * (order[0]-1) + order[1]-1) + (k ? 2 * (order[0]-1 + order[1]-1) : 0) + offset; + index = j; + entityIndex += (j ? 3 : 2); + } + else + { // !kbdy, On k axis + offset += 4 * (order[0]-1) + 4 * (order[1]-1); + dof = (k - 1) + (order[2]-1) * (i ? (j ? 3 : 1) : (j ? 2 : 0)) + offset; + index = k; + entityIndex = (i ? 1 : 0) + (j ? 2 : 0); + } + return std::make_pair(dof, Key{entityIndex, dim-1, index}); + } + + offset += 4 * (order[0]-1 + order[1]-1 + order[2]-1); + if (nbdy == 1) // Face DOF + { + Key faceKey; + if (ibdy) // On i-normal face + { + dof = (j - 1) + ((order[1]-1) * (k - 1)) + (i ? (order[1]-1) * (order[2]-1) : 0) + offset; + entityIndex = (i ? 1 : 0); + faceKey = VtkLagrangePointSetBuilder<K,2>::calcQuadKey(j-1,k-1,{order[1]-2, order[2]-2}).second; + } + else { + offset += 2 * (order[1] - 1) * (order[2] - 1); + if (jbdy) // On j-normal face + { + dof = (i - 1) + ((order[0]-1) * (k - 1)) + (j ? (order[2]-1) * (order[0]-1) : 0) + offset; + entityIndex = (j ? 3 : 2); + faceKey = VtkLagrangePointSetBuilder<K,2>::calcQuadKey(i-1,k-1,{order[0]-2, order[2]-2}).second; + } + else + { // kbdy, On k-normal face + offset += 2 * (order[2]-1) * (order[0]-1); + dof = (i - 1) + ((order[0]-1) * (j - 1)) + (k ? (order[0]-1) * (order[1]-1) : 0) + offset; + entityIndex = (k ? 5 : 4); + faceKey = VtkLagrangePointSetBuilder<K,2>::calcQuadKey(i-1,j-1,{order[0]-2, order[1]-2}).second; + } + } + return std::make_pair(dof, Key{entityIndex, dim-2, faceKey.index()}); + } + + // nbdy == 0: Body DOF + offset += 2 * ((order[1]-1) * (order[2]-1) + (order[2]-1) * (order[0]-1) + (order[0]-1) * (order[1]-1)); + dof = offset + (i - 1) + (order[0]-1) * ((j - 1) + (order[1]-1) * ((k - 1))); + Key innerKey = VtkLagrangePointSetBuilder<K,3>::calcHexKey(i-1,j-1,k-1,{order[0]-2, order[1]-2, order[2]-2}).second; + return std::make_pair(dof, Key{0, dim-3, innerKey.index()}); + } +}; + +}} // end namespace Dune::Impl diff --git a/dune/vtk/utility/test/CMakeLists.txt b/dune/vtk/utility/test/CMakeLists.txt new file mode 100644 index 0000000..f752b18 --- /dev/null +++ b/dune/vtk/utility/test/CMakeLists.txt @@ -0,0 +1,13 @@ +dune_add_test(NAME test-lagrange1 + SOURCES test-lagrange.cc + COMPILE_DEFINITIONS "CHECKDIM=1") + +dune_add_test(NAME test-lagrange2 + SOURCES test-lagrange.cc + COMPILE_DEFINITIONS "CHECKDIM=2") + +dune_add_test(NAME test-lagrange3 + SOURCES test-lagrange.cc + COMPILE_DEFINITIONS "CHECKDIM=3") + +dune_add_test(SOURCES test-lfecache.cc) \ No newline at end of file diff --git a/dune/vtk/utility/test/test-lagrange.cc b/dune/vtk/utility/test/test-lagrange.cc new file mode 100644 index 0000000..62ed50a --- /dev/null +++ b/dune/vtk/utility/test/test-lagrange.cc @@ -0,0 +1,169 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#include <config.h> + +#include <dune/localfunctions/utility/field.hh> +#include <dune/localfunctions/utility/basisprint.hh> + +#include <dune/localfunctions/lagrange/lagrangebasis.hh> +#include <dune/vtk/utility/lagrangepoints.hh> + +/** + * \file + * \brief Performs some tests for the generic Lagrange + * shape functions on simplices. + * + * The topology can be chosen at compile time by setting TOPOLOGY + * to a string like + * \code + * Pyramid<Pyramid<Point> > > + * \endcode + * which generates a 2d simplex. If TOPOLOGY is not set, all + * topologies up to 4d are tested. Note, this may lead to prolonged + * compiler runs. + * + * For debugging purpuse the functions and the derivatives can be + * printed. You have to define the macro TEST_OUTPUT_FUNCTIONS to + * activate this function. + */ + +#if HAVE_GMP +typedef Dune::GMPField< 128 > StorageField; +typedef Dune::GMPField< 512 > ComputeField; +#else +typedef double StorageField; +typedef double ComputeField; +#endif + +template <class Basis,class Points> +bool test(const Basis &basis, const Points &points, bool verbose) +{ + bool ret = true; + std::vector< Dune::FieldVector< double, 1 > > y( basis.size() ); + for( unsigned int index = 0; index < points.size(); ++index ) + { + if (verbose) + std::cout << index << " " << points[ index ].point() << " " + << points[ index ].localKey() + << std::endl; + basis.evaluate( points[ index ].point(), y ); + bool first = true; + for( unsigned int i = 0; i < y.size(); ++i ) + { + if( fabs( y[ i ] - double( i == index ) ) > 1e-10 ) + { + if (first) { + std::cout << "ERROR: " + << index << " -> " + << "x = " << points[ index ].point() + << " (codim = " << points[ index ].localKey().codim() << ", " + << "subentity = " << points[ index ].localKey().subEntity() << ", " + << "index = " << points[ index ].localKey().index() << "):" << std::endl; + first = false; + } + if (1) + std::cout << " y[ " << i << " ] = " << y[ i ] << " " + << " error : " << fabs( y[ i ] - double( i == index ) ) + << std::endl; + ret = false; + } + } + } + return ret; +} + +template <class Topology> +bool test(unsigned int order, bool verbose = false) +{ + typedef Dune::LagrangeBasisFactory<Dune::VtkLagrangePointSet,Topology::dimension,StorageField,ComputeField> BasisFactory; + typedef Dune::LagrangeCoefficientsFactory< Dune::VtkLagrangePointSet, Topology::dimension,double > LagrangeCoefficientsFactory; + + bool ret = true; + + for (unsigned int o = 0; o <= order; ++o) + { + const typename LagrangeCoefficientsFactory::Object *pointsPtr = LagrangeCoefficientsFactory::template create< Topology >( o ); + + if ( pointsPtr == 0) + continue; + + std::cout << "# Testing " << Topology::name() << " in dimension " << Topology::dimension << " with order " << o << std::endl; + + typename BasisFactory::Object &basis = *BasisFactory::template create<Topology>(o); + + ret |= test(basis,*pointsPtr,verbose); + + // define the macro TEST_OUTPUT_FUNCTIONS to output files containing functions and + // derivatives in a human readabible form (aka LaTeX source) +#ifdef TEST_OUTPUT_FUNCTIONS + std::stringstream name; + name << "lagrange_" << Topology::name() << "_p" << o << ".basis"; + std::ofstream out(name.str().c_str()); + Dune::basisPrint<0,BasisFactory,typename BasisFactory::StorageField>(out,basis); + Dune::basisPrint<1,BasisFactory,typename BasisFactory::StorageField>(out,basis); +#endif // TEST_OUTPUT_FUNCTIONS + + LagrangeCoefficientsFactory::release( pointsPtr ); + BasisFactory::release( &basis ); + } + + if (verbose) + std::cout << std::endl << std::endl << std::endl; + if (!ret) { + std::cout << " FAILED !" << std::endl; + } + return ret; +} + +#ifdef CHECKDIM + #if CHECKDIM==1 + #define CHECKDIM1 + #elif CHECKDIM==2 + #define CHECKDIM2 + #elif CHECKDIM==3 + #define CHECKDIM3 + #endif +#else + #define CHECKDIM1 + #define CHECKDIM2 + #define CHECKDIM3 +#endif + + + +int main ( int argc, char **argv ) +{ + using namespace Dune; + using namespace Impl; + + unsigned int order = (argc < 2) ? 5 : atoi(argv[1]); + + if (argc < 2) + { + std::cerr << "Usage: " << argv[ 0 ] << " <p>" << std::endl + << "Using default order of " << order << std::endl; + } +#ifdef TOPOLOGY + return (test<TOPOLOGY>(order) ? 0 : 1 ); +#else + bool tests = true; + +#ifdef CHECKDIM1 + tests &= test<Prism<Point> > (order); // line + tests &= test<Pyramid<Point> > (order); // line +#endif + +#ifdef CHECKDIM2 + tests &= test<Prism<Prism<Point> > > (order); // quad + tests &= test<Pyramid<Pyramid<Point> > >(order); // triangle +#endif + +#ifdef CHECKDIM3 + tests &= test<Prism<Prism<Prism<Point> > > >(order); // hexahedron + // tests &= test<Prism<Pyramid<Pyramid<Point> > > >(order); + tests &= test<Pyramid<Pyramid<Pyramid<Point> > > >(order); // tetrahedron +#endif + + return (tests ? 0 : 1); +#endif // TOPOLOGY +} diff --git a/dune/vtk/utility/test/test-lfecache.cc b/dune/vtk/utility/test/test-lfecache.cc new file mode 100644 index 0000000..2782090 --- /dev/null +++ b/dune/vtk/utility/test/test-lfecache.cc @@ -0,0 +1,56 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <dune/common/hybridutilities.hh> +#include <dune/common/rangeutilities.hh> +#include <dune/common/std/utility.hh> +#include <dune/common/test/testsuite.hh> + +#include <dune/geometry/type.hh> + +#include <dune/vtk/utility/lagrangelfecache.hh> + +template <class FiniteElementCache> +int test (FiniteElementCache& cache, Dune::GeometryType type) +{ + using FiniteElement = typename FiniteElementCache::FiniteElementType; + const FiniteElement& finiteElement = cache.get(type); + + if (!(finiteElement.type() == type)) + DUNE_THROW(Dune::Exception, "Wrong GeometryType in constructed local finite-element"); + + return finiteElement.localBasis().order(); +} + +int main() +{ + using namespace Dune; + const int max_dim = 3; + const unsigned int max_order = 4; + + TestSuite testSuite; + Hybrid::forEach(StaticIntegralRange<int,max_dim+1,1>{},[&](auto dim) + { + // template order parameter + Hybrid::forEach(StaticIntegralRange<int,max_order+1,1>{},[&](auto k) { + using FiniteElementCache = LagrangeLFECache<double, double, dim.value, k.value>; + FiniteElementCache cache{}; + testSuite.check(test(cache, Dune::GeometryTypes::simplex(dim)) == k); + testSuite.check(test(cache, Dune::GeometryTypes::cube(dim)) == k); + }); + + // runtime order parameter + for (unsigned int k = 1; k <= max_order; ++k) { + using FiniteElementCache = LagrangeLFECache<double, double, dim>; + FiniteElementCache cache{k}; + testSuite.check(test(cache, Dune::GeometryTypes::simplex(dim)) == k); + testSuite.check(test(cache, Dune::GeometryTypes::cube(dim)) == k); + } + }); + + return testSuite.exit(); +} diff --git a/dune/vtk/vtkfunction.hh b/dune/vtk/vtkfunction.hh index d745da6..48444ec 100644 --- a/dune/vtk/vtkfunction.hh +++ b/dune/vtk/vtkfunction.hh @@ -22,12 +22,12 @@ namespace Dune class VtkFunction { template <class F> - using HasLocalFunction = decltype(localFunction(std::declval<F>())); + using LocalFunction = decltype(localFunction(std::declval<F>())); using Domain = typename GridView::template Codim<0>::Entity::Geometry::LocalCoordinate; template <class F> - using Range = std::decay_t<decltype(std::declval<F>()(std::declval<Domain>()))>; + using Range = std::decay_t<std::result_of_t<F(Domain)>>; private: @@ -71,14 +71,14 @@ namespace Dune * GridFunction if not given. **/ template <class F, - class = void_t<HasLocalFunction<F>> > + class = void_t<LocalFunction<F>> > VtkFunction (F&& fct, std::string name, Std::optional<int> ncomps = {}, Std::optional<Vtk::DataTypes> type = {}) : localFct_(localFunction(std::forward<F>(fct))) , name_(std::move(name)) { - using R = Range<decltype(localFunction(std::forward<F>(fct)))>; + using R = Range<LocalFunction<F>>; ncomps_ = ncomps ? *ncomps : sizeOf<R>(); type_ = type ? *type : Vtk::Map::type<R>(); @@ -86,7 +86,7 @@ namespace Dune /// Constructor that forward the number of components and data type to the other constructor template <class F, - class = void_t<HasLocalFunction<F>> > + class = void_t<LocalFunction<F>> > VtkFunction (F&& fct, Vtk::FieldInfo fieldInfo, Std::optional<Vtk::DataTypes> type = {}) : VtkFunction(std::forward<F>(fct), fieldInfo.name(), fieldInfo.ncomps(), type) diff --git a/dune/vtk/vtkreader.hh b/dune/vtk/vtkreader.hh index c92bc82..294ced1 100644 --- a/dune/vtk/vtkreader.hh +++ b/dune/vtk/vtkreader.hh @@ -41,13 +41,12 @@ namespace Dune std::uint64_t offset = 0; }; - using Entity = typename Grid::template Codim<0>::Entity; - using GlobalCoordinate = typename Entity::Geometry::GlobalCoordinate; + using GlobalCoordinate = typename GridCreator::GlobalCoordinate; public: /// Constructor. Creates a new GridCreator with the passed factory template <class... Args, - disableCopyMove<VtkReader, Args...> = 0> + std::enable_if_t<std::is_constructible<GridCreator, Args...>::value,int> = 0> explicit VtkReader (Args&&... args) : VtkReader(std::make_shared<GridCreator>(std::forward<Args>(args)...)) {} @@ -76,6 +75,12 @@ namespace Dune reader.readFromFile(filename); } + /// Obtains the creator of the grid + GridCreator& gridCreator () + { + return *creator_; + } + /// Advanced read methods /// @{ diff --git a/dune/vtk/vtkreader.impl.hh b/dune/vtk/vtkreader.impl.hh index 9427cde..7acc338 100644 --- a/dune/vtk/vtkreader.impl.hh +++ b/dune/vtk/vtkreader.impl.hh @@ -292,7 +292,7 @@ template <class IStream, class T, class Sections> Sections readDataArray (IStream& input, std::vector<T>& values, std::size_t max_size, Sections section, Sections parent_section) { - values.reserve(max_size); + values.reserve(max_size < std::size_t(-1) ? max_size : 0); using S = std::conditional_t<(sizeof(T) <= 1), std::uint16_t, T>; // problem when reading chars as ints std::size_t idx = 0; @@ -300,11 +300,27 @@ Sections readDataArray (IStream& input, std::vector<T>& values, std::size_t max_ trim(line); if (line.substr(1,10) == "/DataArray") return parent_section; + if (line[0] == '<') + break; std::istringstream stream(line); S value; for (; stream >> value; idx++) values.push_back(T(value)); + if (idx >= max_size) + break; + } + + return section; +} + +template <class IStream, class Sections> +Sections skipRestOfDataArray (IStream& input, Sections section, Sections parent_section) +{ + for (std::string line; std::getline(input, line);) { + ltrim(line); + if (line.substr(1,10) == "/DataArray") + return parent_section; } return section; @@ -324,6 +340,8 @@ VtkReader<Grid,Creator>::readPoints (std::ifstream& input, std::string name) std::vector<T> point_values; sec = readDataArray(input, point_values, 3*numberOfPoints_, POINTS_DATA_ARRAY, POINTS); + if (sec != POINTS) + sec = skipRestOfDataArray(input, POINTS_DATA_ARRAY, POINTS); assert(sec == POINTS); assert(point_values.size() == 3*numberOfPoints_); @@ -385,7 +403,7 @@ VtkReader<Grid,Creator>::readCells (std::ifstream& input, std::string name) max_size = vec_offsets.back(); else max_size = numberOfCells_ * max_vertices; - sec = readDataArray(input, vec_connectivity, max_size, CELLS_DATA_ARRAY, CELLS); + sec = readDataArray(input, vec_connectivity, std::size_t(-1), CELLS_DATA_ARRAY, CELLS); } else if (name == "global_point_ids") { sec = readDataArray(input, vec_point_ids, numberOfPoints_, CELLS_DATA_ARRAY, CELLS); assert(vec_point_ids.size() == numberOfPoints_); diff --git a/dune/vtk/vtktypes.cc b/dune/vtk/vtktypes.cc index 3ea7b9a..90f993e 100644 --- a/dune/vtk/vtktypes.cc +++ b/dune/vtk/vtktypes.cc @@ -50,6 +50,21 @@ GeometryType to_geometry (std::uint8_t cell) case HEXAHEDRON: return GeometryTypes::hexahedron; case WEDGE: return GeometryTypes::prism; case PYRAMID: return GeometryTypes::pyramid; + + // Quadratic VTK cell types + case QUADRATIC_EDGE: return GeometryTypes::line; + case QUADRATIC_TRIANGLE: return GeometryTypes::triangle; + case QUADRATIC_QUAD: return GeometryTypes::quadrilateral; + case QUADRATIC_TETRA: return GeometryTypes::tetrahedron; + case QUADRATIC_HEXAHEDRON: return GeometryTypes::hexahedron; + + // Arbitrary order Lagrange elements + case LAGRANGE_CURVE: return GeometryTypes::line; + case LAGRANGE_TRIANGLE: return GeometryTypes::triangle; + case LAGRANGE_QUADRILATERAL:return GeometryTypes::quadrilateral; + case LAGRANGE_TETRAHEDRON: return GeometryTypes::tetrahedron; + case LAGRANGE_HEXAHEDRON: return GeometryTypes::hexahedron; + case LAGRANGE_WEDGE: return GeometryTypes::prism; default: DUNE_THROW(RangeError, "CellType does not map to GeometryType."); std::abort(); @@ -153,6 +168,32 @@ CellType::CellType (GeometryType const& t, CellParametrization parametrization) std::cerr << "Geometry Type not supported by VTK!\n"; std::abort(); } + } else if (parametrization == LAGRANGE) { + if (t.isLine()) { + type_ = LAGRANGE_CURVE; + } + else if (t.isTriangle()) { + type_ = LAGRANGE_TRIANGLE; + } + else if (t.isQuadrilateral()) { + type_ = LAGRANGE_QUADRILATERAL; + } + else if (t.isTetrahedron()) { + type_ = LAGRANGE_TETRAHEDRON; + } + else if (t.isHexahedron()) { + type_ = LAGRANGE_HEXAHEDRON; + } + else if (t.isPrism()) { + type_ = LAGRANGE_WEDGE; + } + else if (t.isPyramid()) { + type_ = LAGRANGE_PYRAMID; + } + else { + std::cerr << "Geometry Type not supported by VTK!\n"; + std::abort(); + } } } diff --git a/dune/vtk/vtktypes.hh b/dune/vtk/vtktypes.hh index 3d41852..67f0c49 100644 --- a/dune/vtk/vtktypes.hh +++ b/dune/vtk/vtktypes.hh @@ -33,7 +33,8 @@ namespace Dune enum CellParametrization { LINEAR, - QUADRATIC + QUADRATIC, + LAGRANGE }; enum CellTypes : std::uint8_t { @@ -57,7 +58,15 @@ namespace Dune QUADRATIC_TRIANGLE = 22, QUADRATIC_QUAD = 23, QUADRATIC_TETRA = 24, - QUADRATIC_HEXAHEDRON = 25 + QUADRATIC_HEXAHEDRON = 25, + // Arbitrary order Lagrange elements + LAGRANGE_CURVE = 68, + LAGRANGE_TRIANGLE = 69, + LAGRANGE_QUADRILATERAL = 70, + LAGRANGE_TETRAHEDRON = 71, + LAGRANGE_HEXAHEDRON = 72, + LAGRANGE_WEDGE = 73, + LAGRANGE_PYRAMID = 74, }; GeometryType to_geometry (std::uint8_t); @@ -110,7 +119,7 @@ namespace Dune private: std::uint8_t type_; std::vector<int> permutation_; - bool noPermutation_; + bool noPermutation_ = true; }; diff --git a/dune/vtk/vtkwriterinterface.hh b/dune/vtk/vtkwriterinterface.hh index 877d760..33121d2 100644 --- a/dune/vtk/vtkwriterinterface.hh +++ b/dune/vtk/vtkwriterinterface.hh @@ -92,9 +92,9 @@ namespace Dune * Constructor for possible arguments. **/ template <class Function, class... Args> - VtkWriterInterface& addPointData (Function const& fct, Args&&... args) + VtkWriterInterface& addPointData (Function&& fct, Args&&... args) { - pointData_.emplace_back(fct, std::forward<Args>(args)...); + pointData_.emplace_back(std::forward<Function>(fct), std::forward<Args>(args)...); return *this; } @@ -106,9 +106,9 @@ namespace Dune * See \ref VtkFunction Constructor for possible arguments. **/ template <class Function, class... Args> - VtkWriterInterface& addCellData (Function const& fct, Args&&... args) + VtkWriterInterface& addCellData (Function&& fct, Args&&... args) { - cellData_.emplace_back(fct, std::forward<Args>(args)...); + cellData_.emplace_back(std::forward<Function>(fct), std::forward<Args>(args)...); return *this; } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0ec1e3d..0c09ee2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,3 +1,5 @@ +set(GRID_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../doc/") + dune_add_test(SOURCES vtkreader.cc LINK_LIBRARIES dunevtk) @@ -17,6 +19,14 @@ dune_add_test(SOURCES datacollector.cc LINK_LIBRARIES dunevtk CMAKE_GUARD dune-functions_FOUND) +dune_add_test(SOURCES lagrangepoints.cc + LINK_LIBRARIES dunevtk) + +dune_add_test(SOURCES lagrangereader.cc + COMPILE_DEFINITIONS "GRID_PATH=\"${GRID_PATH}\"" + LINK_LIBRARIES dunevtk + CMAKE_GUARD dune-foamgrid_FOUND) + dune_add_test(SOURCES structuredgridwriter.cc LINK_LIBRARIES dunevtk CMAKE_GUARD dune-functions_FOUND) @@ -37,7 +47,6 @@ dune_add_test(SOURCES vectorwriter.cc LINK_LIBRARIES dunevtk CMAKE_GUARD dune-functions_FOUND) - if (dune-polygongrid_FOUND) # CMAKE_GUARD can not be used, since a dummy target is created and linked against dunepolygongrid dune_add_test(SOURCES polygongrid.cc diff --git a/src/datacollector.cc b/src/datacollector.cc index 607591e..9f126d2 100644 --- a/src/datacollector.cc +++ b/src/datacollector.cc @@ -19,13 +19,19 @@ #include <dune/functions/gridfunctions/analyticgridviewfunction.hh> #include <dune/functions/gridfunctions/discreteglobalbasisfunction.hh> +#if HAVE_UG +#include <dune/grid/uggrid.hh> +#endif + #include <dune/grid/yaspgrid.hh> +#include <dune/grid/utility/structuredgridfactory.hh> #include <dune/vtk/writers/vtkunstructuredgridwriter.hh> #include <dune/vtk/datacollectors/continuousdatacollector.hh> #include <dune/vtk/datacollectors/discontinuousdatacollector.hh> #include <dune/vtk/datacollectors/quadraticdatacollector.hh> +#include <dune/vtk/datacollectors/lagrangedatacollector.hh> using namespace Dune; using namespace Dune::Functions; @@ -45,6 +51,7 @@ void write_dc (std::string prefix, GridView const& gridView, Fct1 const& fct1, F template <class GridView> void write (std::string prefix, GridView const& gridView) { + std::cout << prefix << "..." << std::endl; #if ! DUNE_VERSION_NEWER(DUNE_FUNCTIONS, 2, 6) using namespace BasisBuilder; #else @@ -69,6 +76,10 @@ void write (std::string prefix, GridView const& gridView) write_dc<ContinuousDataCollector<GridView>>(prefix + "_continuous", gridView, p1Interpol, p1Analytic); write_dc<DiscontinuousDataCollector<GridView>>(prefix + "_discontinuous", gridView, p1Interpol, p1Analytic); write_dc<QuadraticDataCollector<GridView>>(prefix + "_quadratic", gridView, p1Interpol, p1Analytic); + + Hybrid::forEach(StaticIntegralRange<int,7,1>{}, [&](auto p) { + write_dc<LagrangeDataCollector<GridView,p>>(prefix + "_lagrange_p" + std::to_string(p), gridView, p1Interpol, p1Analytic); + }); } template <int I> @@ -78,12 +89,24 @@ int main(int argc, char** argv) { Dune::MPIHelper::instance(argc, argv); - Hybrid::forEach(std::make_tuple(int_<1>{}, int_<2>{}, int_<3>{}), [](auto dim) + Hybrid::forEach(StaticIntegralRange<int,4,1>{}, [](auto dim) { using GridType = YaspGrid<dim.value>; FieldVector<double,dim.value> upperRight; upperRight = 1.0; - auto numElements = filledArray<dim.value,int>(8); + auto numElements = filledArray<dim.value,int>(2); GridType grid(upperRight, numElements, 0, 0); write("datacollector_yasp", grid.leafGridView()); }); + +#if HAVE_UG + Hybrid::forEach(StaticIntegralRange<int,4,2>{}, [](auto dim) + { + using GridType = UGGrid<dim.value>; + FieldVector<double,dim.value> lowerLeft; lowerLeft = 0.0; + FieldVector<double,dim.value> upperRight; upperRight = 1.0; + auto numElements = filledArray<dim.value,unsigned int>(4); + auto grid = StructuredGridFactory<GridType>::createSimplexGrid(lowerLeft, upperRight, numElements); + write("datacollector_ug", grid->leafGridView()); + }); +#endif } diff --git a/src/lagrangepoints.cc b/src/lagrangepoints.cc new file mode 100644 index 0000000..a40f83e --- /dev/null +++ b/src/lagrangepoints.cc @@ -0,0 +1,68 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <iostream> +#include <vector> + +#include <dune/common/parallel/mpihelper.hh> // An initializer of MPI +#include <dune/common/indices.hh> + +#include <dune/vtk/utility/lagrangepoints.hh> +#include <dune/localfunctions/lagrange/interpolation.hh> +#include <dune/localfunctions/lagrange/lagrangebasis.hh> +#include <dune/localfunctions/lagrange/lagrangecoefficients.hh> +#include <dune/localfunctions/utility/localfiniteelement.hh> + +using namespace Dune; + +template <std::size_t dim> +void write (std::string prefix, index_constant<dim>) +{ + for (int order = 1; order < 6; ++order) { + std::cout << "order: " << order << std::endl; + { + VtkLagrangePointSet<double, dim> pointSet(order); + pointSet.build(GeometryTypes::cube(dim)); + + std::size_t i = 0; + std::cout << "Cube:" << GeometryTypes::cube(dim) << std::endl; + for (auto const& p : pointSet) { + std::cout << i++ << ") p = " << p.point() << ", key = " << p.localKey() << std::endl; + } + + using BasisF = LagrangeBasisFactory<VtkLagrangePointSet, dim, double, double>; + using CoefficientF = LagrangeCoefficientsFactory<VtkLagrangePointSet, dim, double>; + using InterpolationF = LagrangeInterpolationFactory<VtkLagrangePointSet, dim, double>; + GenericLocalFiniteElement<BasisF, CoefficientF, InterpolationF> localFE(GeometryTypes::cube(dim), order); + + auto const& localBasis = localFE.localBasis(); + auto const& localCoefficints = localFE.localCoefficients(); + auto const& localInterpolation = localFE.localInterpolation(); + } + + { + VtkLagrangePointSet<double, dim> pointSet(order); + pointSet.build(GeometryTypes::simplex(dim)); + + std::size_t i = 0; + std::cout << "Simplex:" << GeometryTypes::simplex(dim) << std::endl; + for (auto const& p : pointSet) { + std::cout << i++ << ") p = " << p.point() << ", key = " << p.localKey() << std::endl; + } + } + } +} + +int main(int argc, char** argv) +{ + Dune::MPIHelper::instance(argc, argv); + + Hybrid::forEach(std::make_tuple(index_constant<2>{}), [](auto dim) + { + write("lagrangepoints", dim); + }); +} diff --git a/src/lagrangereader.cc b/src/lagrangereader.cc new file mode 100644 index 0000000..8a3543c --- /dev/null +++ b/src/lagrangereader.cc @@ -0,0 +1,95 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <iostream> +#include <vector> + +#include <dune/common/parallel/mpihelper.hh> // An initializer of MPI +#include <dune/common/exceptions.hh> // We use exceptions +#include <dune/common/filledarray.hh> +#include <dune/common/test/testsuite.hh> + +// #include <dune/grid/uggrid.hh> +// #include <dune/alugrid/grid.hh> +#include <dune/foamgrid/foamgrid.hh> +#include <dune/geometry/multilineargeometry.hh> +#include <dune/grid/utility/structuredgridfactory.hh> + +#include <dune/vtk/vtkreader.hh> +#include <dune/vtk/datacollectors/lagrangedatacollector.hh> +#include <dune/vtk/gridcreators/lagrangegridcreator.hh> +#include <dune/vtk/writers/vtkunstructuredgridwriter.hh> + +using namespace Dune; + +#ifndef GRID_PATH +#define GRID_PATH +#endif + +int main(int argc, char** argv) +{ + Dune::MPIHelper::instance(argc, argv); + + const int dim = 2; + const int dow = 3; + const int order = 3; + + // using GridType = UGGrid<dim>; + // using GridType = Dune::ALUGrid<dim,dow,Dune::simplex,Dune::conforming>; + using GridType = FoamGrid<dim,dow>; + using GridView = typename GridType::LeafGridView; + using DataCollector = LagrangeDataCollector<GridView, order>; + using GridCreator = LagrangeGridCreator<GridType>; + + std::string filename = "triangles_" + std::to_string(dow) + "d_order" + std::to_string(order); + + TestSuite testSuite; + + { // Test using the (static) file-reader interface + std::cout << "Test 1..." << std::endl; + auto gridPtr = VtkReader<GridType,GridCreator>::read(GRID_PATH "/" + filename + ".vtu"); + auto& grid = *gridPtr; + grid.globalRefine(2); + + VtkUnstructuredGridWriter<GridView,DataCollector> vtkWriter(grid.leafGridView(), Vtk::ASCII); + vtkWriter.write(filename + "_out1.vtu"); + } + + { // Test using an instantiated reader + std::cout << "Test 2..." << std::endl; + GridFactory<GridType> factory; + VtkReader<GridType,GridCreator> reader(factory); + reader.readFromFile(GRID_PATH "/" + filename + ".vtu"); + auto gridPtr = factory.createGrid(); + auto& grid = *gridPtr; + + auto&& creator = reader.gridCreator(); + testSuite.check(creator.order() == order, "order"); + + VtkUnstructuredGridWriter<GridView,DataCollector> vtkWriter(grid.leafGridView(), Vtk::ASCII); + vtkWriter.addPointData(creator, "param", 3); + vtkWriter.write(filename + "_out2.vtu"); + } + + { // Test using an explicit grid-creator + std::cout << "Test 3..." << std::endl; + GridFactory<GridType> factory; + GridCreator creator(factory); + VtkReader<GridType,GridCreator> reader(creator); + reader.readFromFile(GRID_PATH "/" + filename + ".vtu"); + auto gridPtr = factory.createGrid(); + auto& grid = *gridPtr; + + testSuite.check(creator.order() == order, "order"); + + VtkUnstructuredGridWriter<GridView,DataCollector> vtkWriter(grid.leafGridView(), Vtk::ASCII); + vtkWriter.addPointData(creator, "param", 3); + vtkWriter.write(filename + "_out3.vtu"); + } + + return testSuite.exit(); +} -- GitLab