Commit 10b86f6d authored by Dennis Gläser's avatar Dennis Gläser
Browse files

[entitynet] restructure entity networks

This completely restructures the entity network class, which now allows
for iteration over the fragments of the different constituents
(subdomains, entities, intersections, junctions). Moreover, one can
extract connectivity information on intersections and junctions.

The builder and writer classes are adapted accordingly, and are still
used in the same way as before. The entity network class itself was
modified in a non-backwards-compatible way, as the current
implementation did not provide much functionality apart from passing it
onto a writer, which can still be done in the same way.
parent 22ce1e4c
install(FILES
constituents.hh
constraints.hh
constraintsmatrix.hh
containedentitynetwork.hh
containedentitynetworkinterface.hh
entitynetwork.hh
entitynetworkinterface.hh
multigeometryentityset.hh
networkbuilder.hh
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/frackit/entitynetwork)
......@@ -19,66 +19,46 @@
/*!
* \file
* \ingroup EntityNetwork
* \brief Interface defining networks of entities.
* \brief Types that can be used to represent fragments of the different
* constituents of an entity network, that is, the network entities,
* their intersections, junctions of intersections as well as embedding
* domains.
*/
#ifndef FRACKIT_ENTITY_NETWORK_INTERFACE_HH
#define FRACKIT_ENTITY_NETWORK_INTERFACE_HH
#ifndef FRACKIT_ENTITY_NETWORK_CONSTITUENTS_HH
#define FRACKIT_ENTITY_NETWORK_CONSTITUENTS_HH
#include <vector>
#include <unordered_map>
#include <TopTools_DataMapOfShapeInteger.hxx>
#include <TopoDS_Shape.hxx>
namespace Frackit {
namespace Frackit::EntityNetworkConstituents {
/*!
* \ingroup EntityNetwork
* \brief Interface defining networks of entities.
* Defines the mandatory functions required, for instance,
* to write the network into a brep or geo file. Actual
* implementations may provide additional, more user-friendly
* functions.
* \brief Base class to represent constituents of entity networks.
* Consists of a shape that describes a fragment of some primary
* entity, and a unique index within the set of fragments of the
* same sort.
*/
class EntityNetworkInterface
class Constituent
{
public:
//! export underlying map types
using ShapeMap = TopTools_DataMapOfShapeInteger;
using IndexList = std::vector<std::size_t>;
using ConnectivityMap = std::unordered_map<std::size_t, IndexList>;
using Shape = TopoDS_Shape;
//! Abstract base classes need virtual destructors
virtual ~EntityNetworkInterface() {}
/*!
* \brief Constructor.
* \param entityDim Dimension of the network entities
*/
EntityNetworkInterface(int entityDim)
: entityDimension_(entityDim)
{}
/*!
* \brief Returns the dimension of the network entities.
*/
int entityDimension() const
{ return entityDimension_; }
/*!
* \brief The map with all fragments of the network, where
* each fragment is mapped to the original entity index.
*/
virtual const ShapeMap& entityFragmentsMap() const = 0;
/*!
* \brief The map with all intersections, mapping them to unique ids.
*/
virtual const ShapeMap& intersectionFragmentsMap() const = 0;
Constituent(const Shape& s, std::size_t idx) : shape_(s) , index_(idx) {}
Constituent(Shape&& s, std::size_t idx) : shape_(std::move(s)) , index_(idx) {}
const Shape& shape() const { return shape_; }
std::size_t index() const { return index_; }
private:
int entityDimension_;
Shape shape_;
std::size_t index_;
};
} // end namespace Frackit
class SubDomainFragment : public Constituent { public: using Constituent::Constituent; };
class EntityFragment : public Constituent { public: using Constituent::Constituent; };
class IntersectionFragment : public Constituent { public: using Constituent::Constituent; };
class IntersectionJunction : public Constituent { public: using Constituent::Constituent; };
} // end namespace Frackit::EntityNetworkConstituents
#endif // FRACKIT_ENTITY_NETWORK_INTERFACE_HH
#endif // FRACKIT_ENTITY_NETWORK_CONSTITUENTS_HH
......@@ -16,91 +16,27 @@
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*****************************************************************************/
/*!
* \file
* \ingroup EntityNetwork
* \brief Class representing a network of entities, contained
* in (possibly multiple) sub-domains. Sub-networks might
* be defined on each sub-domain.
*/
/*!
* \file
* \ingroup EntityNetwork
* \brief Class representing a network of entities contained in
* one or multiple embedding subdomains.
*/
#ifndef FRACKIT_CONTAINED_ENTITY_NETWORK_HH
#define FRACKIT_CONTAINED_ENTITY_NETWORK_HH
#include <TopTools_DataMapOfShapeInteger.hxx>
#include <TopTools_ListOfShape.hxx>
#include <TopoDS_Shape.hxx>
#include <frackit/common/id.hh>
#include "containedentitynetworkinterface.hh"
#include "entitynetwork.hh"
namespace Frackit {
/*!
* \ingroup EntityNetwork
* \relates ContainedEntityNetworkBuilder
* \brief Class representing a network of entities, contained
* in (possibly multiple) sub-domains. Sub-networks might
* be defined on each sub-domain.
* \note Use the class ContainedEntityNetworkBuilder for construction.
* \brief Class representing a network of entities contained in
* one or multiple embedding subdomains.
* \deprecated Use EntityNetwork instead
*/
class ContainedEntityNetwork
: public ContainedEntityNetworkInterface
{
public:
//! Pull up typedefs
using typename ContainedEntityNetworkInterface::ShapeMap;
using typename ContainedEntityNetworkInterface::ConnectivityMap;
/*!
* \brief Constructor.
* \param entityDim Dimension of the network entities
* \param sdFragmentsMap The map of subdomain fragments -> subdomain index
* \param entityFragmentsMap The map of entity fragments -> primary entity index
*/
ContainedEntityNetwork(int entityDim,
int domainDim,
ShapeMap&& sdFragmentsMap,
ShapeMap&& entityFragmentsMap,
ShapeMap&& intersectionFragmentMap,
ConnectivityMap&& isConnectivityMap)
: ContainedEntityNetworkInterface(entityDim, domainDim)
, subDomainFragmentsMap_(std::move(sdFragmentsMap))
, entityFragmentsMap_(std::move(entityFragmentsMap))
, intersectionFragmentsMap_(std::move(intersectionFragmentMap))
, isConnectivityMap_(std::move(isConnectivityMap))
{}
/*!
* \brief Returns the subdomain fragments map
*/
const ShapeMap& subDomainFragmentsMap() const override
{ return subDomainFragmentsMap_; }
/*!
* \brief Returns the entity fragments map of the network
*/
const ShapeMap& entityFragmentsMap() const override
{ return entityFragmentsMap_; }
/*!
* \brief The map with all intersections, mapping them to unique ids.
*/
virtual const ShapeMap& intersectionFragmentsMap() const override
{ return intersectionFragmentsMap_; }
/*!
* \brief The map of intersection id to intersecting entity indices.
*/
const ConnectivityMap& intersectionConnectivityMap() const
{ return isConnectivityMap_; }
private:
ShapeMap subDomainFragmentsMap_;
ShapeMap entityFragmentsMap_;
ShapeMap intersectionFragmentsMap_;
ConnectivityMap isConnectivityMap_;
};
using ContainedEntityNetwork [[deprecated("Use EntityNetwork instead")]]
= EntityNetwork<>;
} // end namespace Frackit
......
// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
// vi: set et ts=4 sw=4 sts=4:
/*****************************************************************************
* See the file COPYING for full copying permissions. *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*****************************************************************************/
/*!
* \file
* \ingroup EntityNetwork
* \brief Interface defining networks of entities contained
* in (possibly multiple) sub-domains.
*/
#ifndef FRACKIT_CONTAINED_ENTITY_NETWORK_INTERFACE_HH
#define FRACKIT_CONTAINED_ENTITY_NETWORK_INTERFACE_HH
#include <vector>
#include <unordered_map>
#include <TopTools_DataMapOfShapeInteger.hxx>
namespace Frackit {
/*!
* \ingroup EntityNetwork
* \brief Interface defining networks of entities contained
* in (possibly multiple) sub-domains.
* Defines the mandatory functions required, for instance,
* to write the network into a brep or geo file. Actual
* implementations may provide additional, more user-friendly
* functions.
*/
class ContainedEntityNetworkInterface
{
public:
//! export underlying map types
using ShapeMap = TopTools_DataMapOfShapeInteger;
using IndexList = std::vector<std::size_t>;
using ConnectivityMap = std::unordered_map<std::size_t, IndexList>;
//! Abstract base classes need virtual destructors
virtual ~ContainedEntityNetworkInterface() {}
/*!
* \brief Constructor.
* \param entityDim Dimension of the network entities
* \param domainDim Dimension of the domain in
* which entities are embedded
*/
ContainedEntityNetworkInterface(int entityDim, int domainDim)
: entityDimension_(entityDim)
, domainDimension_(domainDim)
{}
/*!
* \brief Returns the dimension of the network entities.
*/
int entityDimension() const
{ return entityDimension_; }
/*!
* \brief Returns the dimension of the (sub-)domains.
*/
int domainDimension() const
{ return domainDimension_; }
/*!
* \brief Returns the fragments map of the subdomains
*/
virtual const ShapeMap& subDomainFragmentsMap() const = 0;
/*!
* \brief Returns the entity fragments map of the network
*/
virtual const ShapeMap& entityFragmentsMap() const = 0;
/*!
* \brief The map with all intersections, mapping them to unique ids.
*/
virtual const ShapeMap& intersectionFragmentsMap() const = 0;
private:
int entityDimension_;
int domainDimension_;
};
} // end namespace Frackit
#endif // FRACKIT_CONTAINED_ENTITY_NETWORK_INTERFACE_HH
This diff is collapsed.
......@@ -50,11 +50,11 @@
namespace Frackit {
// Forward declaration of the builder class for entity networks
template<class ctype = double>
template<class ctype = double, class EN = Frackit::EntityNetwork<>>
class EntityNetworkBuilder;
// Forward declaration of the builder class for contained entity networks
template<class ctype = double>
template<class ctype = double, class EN = Frackit::EntityNetwork<>>
class ContainedEntityNetworkBuilder;
/*!
......@@ -62,15 +62,14 @@ class ContainedEntityNetworkBuilder;
* \brief Base class for builders of entity networks.
* Stores data related to the entities and sub-domains.
*/
template<class ctype = double>
template<class ctype, class EN>
class EntityNetworkBuilderBase
{
protected:
using ShapeList = TopTools_ListOfShape;
using ShapeIdMap = TopTools_DataMapOfShapeInteger;
using ShapeIdMapIt = TopTools_DataMapIteratorOfDataMapOfShapeInteger;
using ConnectivityMap = std::unordered_map<std::size_t, std::vector<std::size_t>>;
using NeighborsMap = std::unordered_map<std::size_t, std::vector<std::size_t>>;
public:
......@@ -249,7 +248,7 @@ protected:
* \brief Computes the initial fragmentation of the network
* resulting from confinement to the (sub-)domains.
*/
ShapeIdMap computeConfinedFragments_()
ShapeIdMap confineEntities_()
{
ShapeIdMap result;
for (const auto& indexNetworkPair : networks_)
......@@ -309,29 +308,18 @@ protected:
}
/*!
* \brief Computes the complete fragmentation of the network and the sub-domains.
* \param omitSubDomains If set to true, the domains will not be
* fragmented. This can be used if one is only
* interested in the entity network and not in the domains.
* \return array with the fragments maps for the entities and subdomains
* \brief Computes the fragmentation of all entities provided in the given maps
* \param maps vector of shape maps with entities that are to be fragmented
* \return vector with the fragments, mapping back to the indices of the given maps
*/
std::array<ShapeIdMap, 2> computeAllFragments_(const ShapeIdMap& confinedShapeMap,
bool omitSubDomains = false)
std::vector<ShapeIdMap> fragment_(const std::vector<const ShapeIdMap*>& maps)
{
// List with all geometries
const auto allEntities = makeShapeList_(confinedShapeMap);
// All sub-domains (omit this if only network is considered)
ShapeIdMap subDomainMap;
if (!omitSubDomains)
for (const auto& idShapePair : subDomains_)
subDomainMap.Bind(idShapePair.second, idShapePair.first);
const auto allSubDomains = makeShapeList_(subDomainMap);
// List of both entity and sub-domain shapes
ShapeList allShapes(allEntities);
for (const auto& shape : allSubDomains)
allShapes.Append(shape);
ShapeList allShapes;
for (auto mapPtr : maps)
{
auto list = makeShapeList_(*mapPtr);
allShapes.Append(list);
}
// fragment all shapes
BRepAlgoAPI_BuilderAlgo fragmentAlgo;
......@@ -342,89 +330,87 @@ protected:
if(!fragmentAlgo.IsDone())
throw std::runtime_error("Could not perform fragmentation");
// map the resulting entities back to the original entity index
ShapeIdMap entityFragments, domainFragments;
for (const auto& shape : allEntities)
// map the resulting entities back to the given indices
std::vector<ShapeIdMap> result(maps.size());
for (unsigned int i = 0; i < maps.size(); ++i)
{
const auto entityIdx = confinedShapeMap.Find(shape);
const bool isDeleted = fragmentAlgo.IsDeleted(shape);
const auto& modified = fragmentAlgo.Modified(shape);
if (!isDeleted && modified.IsEmpty())
entityFragments.Bind(shape, entityIdx);
for (const auto& fragment : modified)
if (!entityFragments.IsBound(fragment))
entityFragments.Bind(fragment, entityIdx);
}
auto& resultMap = result[i];
for (ShapeIdMapIt it(*(maps[i])); it.More(); it.Next())
{
const auto& origShape = it.Key();
const auto& origIndex = it.Value();
// map the resulting domain entities back to the original entity index
for (const auto& domainShape : allSubDomains)
{
const auto subDomainIdx = subDomainMap.Find(domainShape);
const bool isDeleted = fragmentAlgo.IsDeleted(domainShape);
const auto& modified = fragmentAlgo.Modified(domainShape);
if (!isDeleted && modified.IsEmpty())
domainFragments.Bind(domainShape, subDomainIdx);
for (const auto& fragment : modified)
if (!domainFragments.IsBound(fragment))
domainFragments.Bind(fragment, subDomainIdx);
const bool isDeleted = fragmentAlgo.IsDeleted(origShape);
const auto& modified = fragmentAlgo.Modified(origShape);
if (!isDeleted && modified.IsEmpty())
resultMap.Bind(origShape, origIndex);
for (const auto& fragment : modified)
if (!resultMap.IsBound(fragment))
resultMap.Bind(fragment, origIndex);
}
}
return {std::move(entityFragments), std::move(domainFragments)};
return result;
}
/*!
* \brief Returns a tuple of maps, where in the first one the entity
* intersections are mapped to corresponding unique identifiers,
* and in the second one, the intersection identigiers map to the
* primary entities that take part in the intersection.
* \brief Returns a tuple of maps, where:
* First map: entity intersection -> unique identifiers
* Second map: isection identifier -> primary ids of the adjacent fragments
*/
std::tuple<ShapeIdMap, ConnectivityMap>
computeIntersectionData_(const ShapeIdMap& fragments)
std::tuple<ShapeIdMap, NeighborsMap> getIntersections_(const ShapeIdMap& fragments,
unsigned int fragmentDim)
{
ShapeIdMap isShapes;
ConnectivityMap isConnectivity;
std::size_t curIsIdx = 1;
NeighborsMap isNeighbors;
std::size_t curIsId = 1;
// TODO: Start second iterator from ++it, somehow. A first implementation
// of that led to some intersection edges not being meshed at the end
for (ShapeIdMapIt it(fragments); it.More(); it.Next())
for (ShapeIdMapIt it1(fragments); it1.More(); it1.Next())
{
for (ShapeIdMapIt it2(fragments); it2.More(); it2.Next())
ShapeIdMapIt it2(it1);
for (it2.Next(); it2.More(); it2.Next())
{
const auto eIdx1 = it.Value();
const auto eIdx2 = it2.Value();
if (eIdx1 == eIdx2)
continue;
const auto& f1 = TopoDS::Face(it.Key());
const auto& f2 = TopoDS::Face(it2.Key());
const auto edges = OCCUtilities::getCommonEdges(f1, f2);
for (const auto& edge : edges)
for (const auto& s : getCommonShapes_(it1.Key(), it2.Key(), fragmentDim))
{
int isIdx;
if (!isShapes.Find(OCCUtilities::getShape(edge), isIdx))
{
isIdx = curIsIdx;
isShapes.Bind(edge, isIdx);
curIsIdx++;
}
isConnectivity[isIdx].push_back(eIdx1);
isConnectivity[isIdx].push_back(eIdx2);
int isId;
if (!isShapes.Find(s, isId))
{ isId = curIsId++; isShapes.Bind(s, isId); }
isNeighbors[isId].push_back(it1.Value());
isNeighbors[isId].push_back(it2.Value());
}
}
}
// make connectivity maps unique
for (auto& pair : isConnectivity)
// make neighbor maps unique
for (auto& [isId, neighbors] : isNeighbors)
{
auto& list = pair.second;
std::sort(list.begin(), list.end());
list.erase(std::unique(list.begin(), list.end()), list.end());
std::sort(neighbors.begin(), neighbors.end());
auto endIt = std::unique(neighbors.begin(), neighbors.end());
neighbors.erase(endIt, neighbors.end());
}
return {std::move(isShapes), std::move(isConnectivity)};
return {std::move(isShapes), std::move(isNeighbors)};
}
/*!
* \brief Returns a vector with the shapes of codimension 1
* that are common to two given shapes
*/
std::vector<TopoDS_Shape> getCommonShapes_(const TopoDS_Shape& s1,
const TopoDS_Shape& s2,
unsigned int dim)
{
auto cast = [] (const auto& v) { return std::vector<TopoDS_Shape>{v.begin(), v.end()}; };
using namespace OCCUtilities;
const auto boundaryDim = dim - 1;
if (boundaryDim == 0) return cast(getCommonVertices(s1, s2));
if (boundaryDim == 1) return cast(getCommonEdges(s1, s2));
if (boundaryDim == 2) return cast(getCommonFaces(s1, s2));
throw std::runtime_error("Invalid boundary dimension provided");
}
/*!
......@@ -501,16 +487,13 @@ protected:
* the network is embedded in and only carries
* information about the network itself.
*/
template<class ctype>
template<class ctype, class EN>
class EntityNetworkBuilder
: public EntityNetworkBuilderBase<ctype>
: public EntityNetworkBuilderBase<ctype, EN>
{
using ParentType = EntityNetworkBuilderBase<ctype>;
using typename ParentType::ShapeIdMapIt;
public:
//! Export network type to be built
using EntityNetwork = Frackit::EntityNetwork;
using EntityNetwork = EN;
/*!
* \brief Adds an entity to the network.
......@@ -559,20 +542,16 @@ public:
*/
const EntityNetwork& build()
{
// first confine each network to its domain
const auto confined = this->computeConfinedFragments_();
// Then fragment the network entities
auto fragments = this->computeAllFragments_(confined, true)[0];
// get intersection data
auto [isMap, isConnectivity] = this->computeIntersectionData_(fragments);
const auto confinedEntities = this->confineEntities_();
const auto entityFragments = this->fragment_({&confinedEntities})[0];
auto [isections, isMap] = this->getIntersections_(entityFragments, this->entityDimension_);
auto [junctions, jMap] = this->getIntersections_(isections, this->entityDimension_-1);
// create the network
network_ = std::make_unique<EntityNetwork>(this->entityDimension_,
std::move(fragments),
std::move(isMap),
std::move(isConnectivity));
entityFragments, isections,
junctions, isMap, jMap);
this->built_ = true;
return *network_;
}
......@@ -596,17 +575,17 @@ private:
* \brief Builder class for entity networks contained
* in (possibly multiple) sub-domains.
*/
template<class ctype>