Commit 2d907608 authored by Dennis Gläser's avatar Dennis Gläser
Browse files

[test] test new entity networks

parent 890a496f
Pipeline #2763 passed with stages
in 12 minutes and 47 seconds
......@@ -26,5 +26,9 @@ frackit_add_test(NAME test_generate_disk_network_cylinder
frackit_add_test(NAME test_generate_disk_network_shape
SOURCES test_generate_disk_network_shape.cc
LABELS entitynetwork sampling
COMPILE_DEFINITIONS BREPFILE="${CMAKE_SOURCE_DIR}/test/entitynetwork/layer.brep"
COMMAND ./test_generate_disk_network_shape)
COMPILE_DEFINITIONS BREPFILE="${CMAKE_SOURCE_DIR}/test/entitynetwork/layer.brep")
# test entity network classes & corresponding I/O
frackit_add_test(NAME test_entity_network_3d
SOURCES test_entity_network_3d.cc
LABELS entitynetwork)
#include <iostream>
#include <stdexcept>
#include <algorithm>
#include <unordered_map>
#include <type_traits>
#include <utility>
#include <vector>
#include <cmath>
#include <BRepTools.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Shape.hxx>
#include <frackit/common/id.hh>
#include <frackit/geometry/box.hh>
#include <frackit/geometry/quadrilateral.hh>
#include <frackit/magnitude/magnitude.hh>
#include <frackit/occ/breputilities.hh>
#include <frackit/entitynetwork/networkbuilder.hh>
#include <frackit/io/brepwriter.hh>
// helper functions
template<class List, class Value>
bool contains(const List& list, const Value& v);
void testEquality(const Frackit::Id& obtained, const Frackit::Id& expected);
void testEquality(const Frackit::IdPair& obtained, const Frackit::IdPair& expected);
template<class ShapeType>
bool testShapeEquality(const ShapeType& s1, const ShapeType& s2);
template<class Network>
void testNetworkIds(const Network& network,
const std::vector<std::size_t>& expDomainIds,
const std::vector<std::size_t>& expEntityIds,
const std::vector<std::size_t>& expIntersectionIds,
const std::vector<std::size_t>& expJunctionIds)
{
using namespace Frackit;
std::vector<std::size_t> foundIds;
for (const auto& domainFragment : subDomainFragments(network))
{
foundIds.push_back(network.subDomainId(domainFragment).get());
if (!contains(expDomainIds, foundIds.back()))
throw std::runtime_error("Unexpected subdomain id");
}
if (std::any_of(expDomainIds.begin(), expDomainIds.end(),
[&] (auto id) { return !contains(foundIds, id); }))
throw std::runtime_error("Subdomain id not found");
for (auto id : foundIds)
for (const auto& domainFragment : subDomainFragments(network, Id(id)))
if (network.subDomainId(domainFragment).get() != id)
throw std::runtime_error("Subdomain id mismatch");
foundIds.clear();
for (const auto& entityFragment : entityFragments(network))
{
foundIds.push_back(network.entityId(entityFragment).get());
if (!contains(expEntityIds, foundIds.back()))
throw std::runtime_error("Unexpected entity id");
}
if (std::any_of(expEntityIds.begin(), expEntityIds.end(),
[&] (auto id) { return !contains(foundIds, id); }))
throw std::runtime_error("Entity id not found");
for (auto id : foundIds)
for (const auto& entityFragment : entityFragments(network, Id(id)))
if (network.entityId(entityFragment).get() != id)
throw std::runtime_error("Entity id mismatch");
foundIds.clear();
for (const auto& intersectionFragment : intersectionFragments(network))
{
foundIds.push_back(network.intersectionId(intersectionFragment).get());
if (!contains(expIntersectionIds, foundIds.back()))
throw std::runtime_error("Unexpected intersection id");
}
if (std::any_of(expIntersectionIds.begin(), expIntersectionIds.end(),
[&] (auto id) { return !contains(foundIds, id); }))
throw std::runtime_error("Intersection id not found");
for (auto id : foundIds)
for (const auto& intersectionFragment : intersectionFragments(network, Id(id)))
if (network.intersectionId(intersectionFragment).get() != id)
throw std::runtime_error("Intersection id mismatch");
foundIds.clear();
for (const auto& junction : intersectionJunctions(network))
{
foundIds.push_back(network.intersectionJunctionId(junction).get());
if (!contains(expJunctionIds, foundIds.
back()))
throw std::runtime_error("Unexpected junction id");
}
if (std::any_of(expJunctionIds.begin(), expJunctionIds.end(),
[&] (auto id) { return !contains(foundIds, id); }))
throw std::runtime_error("Junction id not found");
}
template<class Network>
void testNetworkSize(const Network& network, const std::array<std::size_t, 8>& expCounts)
{
// first check the result of the size query functions
if (network.numSubDomains() != expCounts[1]) throw std::runtime_error("numSubDomains() failed");
if (network.numSubDomainFragments() != expCounts[0]) throw std::runtime_error("numSubDomainFragments() failed");
if (network.numEntities() != expCounts[3]) throw std::runtime_error("numEntities() failed");
if (network.numEntityFragments() != expCounts[2]) throw std::runtime_error("numEntityFragments() failed");
if (network.numIntersections() != expCounts[5]) throw std::runtime_error("numIntersections() failed");
if (network.numIntersectionFragments() != expCounts[4]) throw std::runtime_error("numIntersectionFragments() failed");
if (network.numIntersectionJunctions() != expCounts[6]) throw std::runtime_error("numIntersectionJunctions() failed");
if (network.numIntersectionJunctions() != expCounts[7]) throw std::runtime_error("numIntersectionJunctions() failed");
std::array<std::size_t, 8> counts;
std::fill(counts.begin(), counts.end(), 0);
std::vector<std::size_t> found;
for (const auto& domainFragment : subDomainFragments(network))
{
counts[0]++;
const auto id = network.subDomainId(domainFragment).get();
if (!contains(found, id)) { found.push_back(id); counts[1]++; }
}
found.clear();
for (const auto& entityFragment : entityFragments(network))
{
counts[2]++;
const auto id = network.entityId(entityFragment).get();
if (!contains(found, id)) { found.push_back(id); counts[3]++; }
}
found.clear();
for (const auto& isFragment : intersectionFragments(network))
{
counts[4]++;
const auto id = network.intersectionId(isFragment).get();
if (!contains(found, id)) { found.push_back(id); counts[5]++; }
}
found.clear();
for (const auto& junction : intersectionJunctions(network))
{
counts[6]++;
const auto id = network.intersectionJunctionId(junction).get();
if (!contains(found, id)) { found.push_back(id); counts[7]++; }
}
auto printArray = [] (const std::string& pre, const auto& a)
{ std::cout << pre; for (auto v : a) std::cout << v << ", "; std::cout << std::endl; };
printArray("Counts: ", counts);
printArray("Expected: ", expCounts);
for (int i = 0; i < 8; ++i)
if (counts[i] != expCounts[i])
throw std::runtime_error("Entity count mismatch");
}
template<class Network>
void testNetworkMagnitudes(const Network& network,
const std::vector<std::pair<double, int>>& volumes,
const std::vector<std::pair<double, int>>& areas,
const std::vector<std::pair<double, int>>& lengths)
{
using std::abs;
auto isEqual = [] (auto a, auto b) { return abs(a-b) < 1e-6; };
auto matchesCount = [&isEqual] (const auto& v, auto val, int count)
{
return count == std::count_if(v.begin(), v.end(),
[&] (auto vVal)
{ return isEqual(val, vVal); });
};
auto compare = [&] (const std::string& magName, const auto& found, const auto& expected)
{
std::cout << "Found " << magName << ": "; for (auto m : found) { std::cout << m << ", "; } std::cout << std::endl;
std::cout << "Expected " << magName << ": "; for (auto [m, count] : expected) std::cout << "{" << m << ", " << count << "}, "; std::cout << std::endl;
for (auto [mag, count] : expected)
if (!matchesCount(found, mag, count))
throw std::runtime_error("Magnitude comparison failed");
};
std::vector<double> found;
for (const auto& domainFragment : subDomainFragments(network))
found.push_back(Frackit::computeMagnitude(TopoDS::Solid(domainFragment.shape())));
compare("volumes", found, volumes);
found.clear();
for (const auto& entityFragment : entityFragments(network))
found.push_back(Frackit::computeMagnitude(TopoDS::Face(entityFragment.shape())));
compare("areas", found, areas);
found.clear();
for (const auto& isFragment : intersectionFragments(network))
found.push_back(Frackit::computeMagnitude(TopoDS::Edge(isFragment.shape())));
compare("lengths", found, lengths);
}
template<class Network>
void testNetworkAdjacency(const Network& network)
{
for (const auto& e : entityFragments(network))
{
int count = 0;
for (const auto& i : embeddedFragments(network, e))
{
count++; bool found = false;
for (const auto& ad : adjacentFragments(network, i))
if (ad.index() == e.index())
found = true;
if (!found) throw std::runtime_error("Adjacency map not symmetric");
}
if (count != 2) throw std::runtime_error("Unexpected embedded isection count");
}
std::vector<std::size_t> counts;
for (const auto& i : intersectionFragments(network))
{
int count = 0;
for (const auto& j : embeddedFragments(network, i))
{
count++; bool found = false;
for (const auto& ad : adjacentFragments(network, j))
if (ad.index() == i.index())
found = true;
if (!found) throw std::runtime_error("Adjacency map not symmetric");
}
counts.push_back(count);
}
auto throwErr = [] () { throw std::runtime_error("Unexpected junction count"); };
const auto numZero = std::count(counts.begin(), counts.end(), 0);
const auto numOnes = std::count(counts.begin(), counts.end(), 1);
if (numOnes != 6) throwErr();
if (numZero != 0 && numZero != numOnes) throwErr();
}
template<class Network>
void testBRep(const Network& network)
{
using namespace Frackit;
BRepWriter writer(network);
writer.write("tmp");
using namespace OCCUtilities;
const auto brepShape = readShape("tmp.brep");
const auto brepVertices = getVertices(brepShape);
const auto brepEdges = getEdges(brepShape);
const auto brepFaces = getFaces(brepShape);
const auto brepSolids = getSolids(brepShape);
for (const auto& domainFragment : subDomainFragments(network))
{
const auto& solid = TopoDS::Solid(domainFragment.shape());
if (std::none_of(brepSolids.begin(), brepSolids.end(),
[&] (const auto& s)
{ return testShapeEquality(s, solid); }))
throw std::runtime_error("Solid not found in brep");
}
std::cout << "All solids found in brep" << std::endl;
for (const auto& entFragment : entityFragments(network))
{
const auto& face = TopoDS::Face(entFragment.shape());
if (std::none_of(brepFaces.begin(), brepFaces.end(),
[&] (const auto& s)
{ return testShapeEquality(s, face); }))
throw std::runtime_error("Face not found in brep");
}
std::cout << "All faces found in brep" << std::endl;
for (const auto& isFragment : intersectionFragments(network))
{
const auto& edge = TopoDS::Edge(isFragment.shape());
if (std::none_of(brepEdges.begin(), brepEdges.end(),
[&] (const auto& s)
{ return testShapeEquality(s, edge); }))
throw std::runtime_error("Edge not found in brep");
}
std::cout << "All edges found in brep" << std::endl;
for (const auto& junction : intersectionJunctions(network))
{
const auto& vertex = TopoDS::Vertex(junction.shape());
if (std::none_of(brepVertices.begin(), brepVertices.end(),
[&] (const auto& s)
{ return testShapeEquality(s, vertex); }))
throw std::runtime_error("Vertex not found in brep");
}
std::cout << "All vertices found in brep" << std::endl;
}
// test entity networks & builder
// this also tests the brepwriter for entity networks
int main()
{
using namespace Frackit;
using ctype = double;
// test 2d entities in 3d domain
using Point = Point<ctype, 3>;
using Quad = Quadrilateral<ctype, 3>;
using Box = Box<ctype>;
// quads split the domain into 8 blocks
Box domain(Point(0.0, 0.0, 0.0), Point(1.0, 1.0, 1.0));
Quad quad1(Point(-0.5, 0.5, -0.5), Point(1.5, 0.5, -0.5),
Point(-0.5, 0.5, 1.5), Point(1.5, 0.5, 1.5));
Quad quad2(Point(0.5, -0.5, -0.5), Point(0.5, 1.5, -0.5),
Point(0.5, -0.5, 1.5), Point(0.5, 1.5, 1.5));
Quad quad3(Point(-0.5, -0.5, 0.5), Point(1.5, -0.5, 0.5),
Point(-0.5, 1.5, 0.5), Point(1.5, 1.5, 0.5));
std::vector<Quad> quadVec{{quad1, quad2, quad3}};
auto range = [] (std::size_t start, std::size_t end)
{
std::vector<std::size_t> v(end-start+1);
for (std::size_t i = 0; i < v.size(); ++i)
v[i] = start + i;
return v;
};
{ // test uncontained build
EntityNetworkBuilder<ctype> unconfinedBuilder;
unconfinedBuilder.setConfineToSubDomainUnion(false);
EntityNetworkBuilder<ctype> confinedBuilder;
confinedBuilder.addSubDomain(domain, Id(1));
////////////////////////////////////////////
// test expected result from add functions
testEquality(confinedBuilder.addSubDomainEntities(quadVec, Id(1)), IdPair(Id(1), Id(3)));
testEquality(unconfinedBuilder.addEntity(quad1), Id(1));
testEquality(unconfinedBuilder.addEntity(quad2), Id(2));
testEquality(unconfinedBuilder.addEntity(quad3), Id(3));
unconfinedBuilder.clear();
testEquality(unconfinedBuilder.addEntities(quadVec), IdPair(Id(1), Id(3)));
///////////////////////////////////////////
std::cout << "Testing confined" << std::endl;
auto n = confinedBuilder.build();
testNetworkSize(n, {0, 0, 12, 3, 6, 6, 1, 1});
testNetworkIds(n, {}, range(1, 3), range(1, 6), range(1, 1));
testNetworkMagnitudes(n, {{0, 0}}, {{0.25, 12}}, {{0.5, 6}});
testNetworkAdjacency(n);
testBRep(n);
std::cout << "\nTesting unconfined" << std::endl;
n = unconfinedBuilder.build();
testNetworkIds(n, {}, range(1, 3), range(1, 6), range(1, 1));
testNetworkSize(n, {0, 0, 12, 3, 6, 6, 1, 1});
testNetworkMagnitudes(n, {{0, 0}}, {{1.0, 12}}, {{1.0, 6}});
testNetworkAdjacency(n);
testBRep(n);
}
{ // test contained build
ContainedEntityNetworkBuilder<ctype> confinedBuilder;
confinedBuilder.addSubDomain(domain, Id(1));
confinedBuilder.addSubDomainEntities(quadVec, Id(1));
ContainedEntityNetworkBuilder<ctype> unconfinedBuilder;
unconfinedBuilder.setConfineToSubDomainUnion(false);
// test expected result from add functions
testEquality(unconfinedBuilder.addSubDomainEntity(quad1, Id(1)), Id(1));
testEquality(unconfinedBuilder.addSubDomainEntity(quad2, Id(1)), Id(2));
testEquality(unconfinedBuilder.addSubDomainEntity(quad3, Id(1)), Id(3));
unconfinedBuilder.clear();
unconfinedBuilder.addSubDomain(domain, Id(1));
testEquality(unconfinedBuilder.addSubDomainEntities(quadVec, Id(1)), IdPair(Id(1), Id(3)));
std::cout << "\nTesting confined (contained)" << std::endl;
auto n = confinedBuilder.build();
testNetworkIds(n, range(1, 1), range(1, 3), range(1, 6), range(1, 1));
testNetworkSize(n, {8, 1, 12, 3, 6, 6, 1, 1});
testNetworkMagnitudes(n, {{0.125, 8}}, {{0.25, 12}}, {{0.5, 6}});
testNetworkAdjacency(n);
testBRep(n);
std::cout << "\nTesting unconfined (contained)" << std::endl;
n = unconfinedBuilder.build();
testNetworkIds(n, range(1, 1), range(1, 3), range(1, 6), range(1, 1));
testNetworkSize(n, {8, 1, 24, 3, 12, 6, 1, 1});
testNetworkMagnitudes(n, {{0.125, 8}}, {{0.25, 12}, {0.75, 12}}, {{0.5, 12}});
testNetworkAdjacency(n);
testBRep(n);
}
std::cout << "All tests passed" << std::endl;
return 0;
}
////////////////////////////////////////////
// Implementations of some helper functions
template<class List, class Value> bool contains(const List& list, const Value& v)
{ return std::count(list.begin(), list.end(), v); }
void testEquality(const Frackit::Id& obtained, const Frackit::Id& expected)
{
if (obtained != expected)
{
std::cout << "Expected: " << expected.get() << std::endl;
std::cout << "Found: " << expected.get() << std::endl;
throw std::runtime_error("Id does not match");
}
}
void testEquality(const Frackit::IdPair& obtained, const Frackit::IdPair& expected)
{
if (obtained.first() != expected.first() || obtained.second() != expected.second())
{
std::cout << "Expected: " << expected.first().get() << " - " << expected.second().get() << std::endl;
std::cout << "Found: " << obtained.first().get() << " - "
<< obtained.second().get() << std::endl;
throw std::runtime_error("Id pair does not match");
}
}
template<class ShapeType>
ShapeType extractSingleShape(const TopoDS_Shape& s)
{
auto checkLength = [] (auto& v) { if (v.size() != 1) throw std::runtime_error("Size"); };
using namespace Frackit::OCCUtilities;
if constexpr (std::is_same_v<ShapeType, TopoDS_Vertex>) { auto v = getVertices(s); checkLength(v); return TopoDS::Vertex(v[0]); }
if constexpr (std::is_same_v<ShapeType, TopoDS_Edge>) { auto v = getEdges(s); checkLength(v); return TopoDS::Edge(v[0]); }
if constexpr (std::is_same_v<ShapeType, TopoDS_Face>) { auto v = getFaces(s); checkLength(v); return TopoDS::Face(v[0]); }
if constexpr (std::is_same_v<ShapeType, TopoDS_Solid>) { auto v = getSolids(s); checkLength(v); return TopoDS::Solid(v[0]); }
throw std::runtime_error("Unsupported shape type");
}
// we check equality based on the intersection and resulting magnitude
// rather than using the isEqual method. This is because we want to compare
// the shapes of a network to those that were read back in after writin it
// to a brep file. That seems to lead to IsEqual() to fail between a shape
// in the memory and one that was read back in from a file
template<class ShapeType>
bool testShapeEquality(const ShapeType& s1, const ShapeType& s2)
{
const auto m1 = Frackit::computeMagnitude(s1);
const auto m2 = Frackit::computeMagnitude(s2);
using std::abs;
if (abs(m2-m1) > 1e-6) return false;
const auto is = Frackit::OCCUtilities::intersect(s1, s2, 1e-6);
if (is.IsNull()) return false;
ShapeType isShape;
try { isShape = extractSingleShape<ShapeType>(is); }
catch (...) { return false; }
return abs(Frackit::computeMagnitude(isShape) - m1) < 1e-6;
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment