WIP: network R extractor, needs testing

This commit is contained in:
Matthias Koefferlein 2025-05-03 22:36:39 +02:00
parent f0943dea53
commit bc10bb6b14
12 changed files with 1453 additions and 622 deletions

View File

@ -10,12 +10,18 @@ SOURCES = \
pexForceLink.cc \
pexRExtractor.cc \
gsiDeclRExtractor.cc \
pexRExtractorTech.cc \
pexRNetExtractor.cc \
pexRNetwork.cc \
pexSquareCountingRExtractor.cc \
pexTriangulationRExtractor.cc
HEADERS = \
pexForceLink.h \
pexRExtractor.h \
pexRExtractorTech.h \
pexRNetExtractor.h \
pexRNetwork.h \
pexSquareCountingRExtractor.h \
pexTriangulationRExtractor.h

View File

@ -22,292 +22,10 @@
#include "pexRExtractor.h"
#include "tlEquivalenceClusters.h"
namespace pex
{
// -----------------------------------------------------------------------------
std::string
RNode::to_string () const
{
std::string res;
switch (type) {
default:
res += "$" + tl::to_string (port_index);
break;
case VertexPort:
res += "V" + tl::to_string (port_index);
break;
case PolygonPort:
res += "P" + tl::to_string (port_index);
break;
}
return res;
}
// -----------------------------------------------------------------------------
std::string
RElement::to_string () const
{
std::string na;
if (a ()) {
na = a ()->to_string ();
} else {
na = "(nil)";
}
std::string nb;
if (b ()) {
nb = b ()->to_string ();
} else {
nb = "(nil)";
}
if (nb < na) {
std::swap (na, nb);
}
std::string res = "R " + na + " " + nb + " ";
res += tl::sprintf ("%.6g", resistance ());
return res;
}
// -----------------------------------------------------------------------------
RNetwork::RNetwork ()
{
// .. nothing yet ..
}
RNetwork::~RNetwork ()
{
clear ();
}
std::string
RNetwork::to_string () const
{
std::string res;
for (auto e = m_elements.begin (); e != m_elements.end (); ++e) {
if (! res.empty ()) {
res += "\n";
}
res += e->to_string ();
}
return res;
}
void
RNetwork::clear ()
{
m_elements.clear (); // must happen before m_nodes
m_nodes.clear ();
m_elements_by_nodes.clear ();
m_nodes_by_type.clear ();
}
RNode *
RNetwork::create_node (RNode::node_type type, unsigned int port_index)
{
if (type != RNode::Internal) {
auto i = m_nodes_by_type.find (std::make_pair (type, port_index));
if (i != m_nodes_by_type.end ()) {
return i->second;
} else {
RNode *new_node = new RNode (this, type, db::DBox (), port_index);
m_nodes.push_back (new_node);
m_nodes_by_type.insert (std::make_pair (std::make_pair (type, port_index), new_node));
return new_node;
}
} else {
RNode *new_node = new RNode (this, type, db::DBox (), port_index);
m_nodes.push_back (new_node);
return new_node;
}
}
RElement *
RNetwork::create_element (double conductance, RNode *a, RNode *b)
{
std::pair<RNode *, RNode *> key (a, b);
if (size_t (b) < size_t (a)) {
std::swap (key.first, key.second);
}
auto i = m_elements_by_nodes.find (key);
if (i != m_elements_by_nodes.end ()) {
if (conductance == pex::RElement::short_value () || i->second->conductance == pex::RElement::short_value ()) {
i->second->conductance = pex::RElement::short_value ();
} else {
i->second->conductance += conductance;
}
return i->second;
} else {
RElement *element = new RElement (this, conductance, a, b);
m_elements.push_back (element);
m_elements_by_nodes.insert (std::make_pair (key, element));
a->m_elements.push_back (element);
element->m_ia = --a->m_elements.end ();
b->m_elements.push_back (element);
element->m_ib = --b->m_elements.end ();
return element;
}
}
void
RNetwork::remove_node (RNode *node)
{
tl_assert (node->type == RNode::Internal);
while (! node->m_elements.empty ()) {
delete const_cast<RElement *> (node->m_elements.front ());
}
delete node;
}
void
RNetwork::remove_element (RElement *element)
{
RNode *a = const_cast<RNode *> (element->a ());
RNode *b = const_cast<RNode *> (element->b ());
delete element;
if (a && a->type == RNode::Internal && a->m_elements.empty ()) {
delete a;
}
if (b && b->type == RNode::Internal && b->m_elements.empty ()) {
delete b;
}
}
void
RNetwork::join_nodes (RNode *a, RNode *b)
{
for (auto e = b->elements ().begin (); e != b->elements ().end (); ++e) {
RNode *on = const_cast<RNode *> ((*e)->other (b));
if (on != a) {
create_element ((*e)->conductance, on, a);
}
}
a->location += b->location;
remove_node (b);
}
void
RNetwork::simplify ()
{
bool any_change = true;
while (any_change) {
any_change = false;
// join shorted clusters - we take care to remove internal nodes only
tl::equivalence_clusters<const RNode *> clusters;
for (auto e = m_elements.begin (); e != m_elements.end (); ++e) {
if (e->conductance == pex::RElement::short_value () && (e->a ()->type == pex::RNode::Internal || e->b ()->type == pex::RNode::Internal)) {
clusters.same (e->a (), e->b ());
}
}
for (size_t ic = 1; ic <= clusters.size (); ++ic) {
RNode *remaining = 0;
RNode *first_node = 0;
for (auto c = clusters.begin_cluster (ic); c != clusters.end_cluster (ic); ++c) {
RNode *n = const_cast<RNode *> ((*c)->first);
if (! first_node) {
first_node = n;
}
if (n->type != pex::RNode::Internal) {
remaining = n;
break;
}
}
if (! remaining) {
// Only internal nodes
remaining = first_node;
}
for (auto c = clusters.begin_cluster (ic); c != clusters.end_cluster (ic); ++c) {
RNode *n = const_cast<RNode *> ((*c)->first);
if (n != remaining && n->type == pex::RNode::Internal) {
any_change = true;
join_nodes (remaining, n);
}
}
}
// combine serial resistors if connected through an internal node
std::vector<RNode *> nodes_to_remove;
for (auto n = m_nodes.begin (); n != m_nodes.end (); ++n) {
size_t nres = n->elements ().size ();
if (n->type == pex::RNode::Internal && nres <= 2) {
any_change = true;
if (nres == 2) {
auto e = n->elements ().begin ();
RNode *n1 = const_cast<RNode *> ((*e)->other (n.operator-> ()));
double r1 = (*e)->resistance ();
++e;
RNode *n2 = const_cast<RNode *> ((*e)->other (n.operator-> ()));
double r2 = (*e)->resistance ();
double r = r1 + r2;
if (r == 0.0) {
create_element (pex::RElement::short_value (), n1, n2);
} else {
create_element (1.0 / r, n1, n2);
}
}
nodes_to_remove.push_back (n.operator-> ());
}
}
for (auto n = nodes_to_remove.begin (); n != nodes_to_remove.end (); ++n) {
remove_node (*n);
}
}
}
// -----------------------------------------------------------------------------
RExtractor::RExtractor ()
{
// .. nothing yet ..

View File

@ -26,352 +26,12 @@
#include "pexCommon.h"
#include "dbPolygon.h"
#include "dbPLC.h"
#include "tlList.h"
#include <string>
#include <list>
#include <limits>
namespace pex
{
class RElement;
class RNode;
class RNetwork;
/**
* @brief Represents a node in the R graph
*
* A node connects to multiple elements (resistors).
* Every element has two nodes. The nodes and elements form
* a graph.
*
* RNode object cannot be created directly. Use "create_node"
* from RNetwork.
*/
struct PEX_PUBLIC RNode
: public tl::list_node<RNode>
{
public:
/**
* @brief The type of the node
*/
enum node_type {
Internal, // an internal node, not related to a port
VertexPort, // a node related to a vertex port
PolygonPort // a node related to a polygon port
};
/**
* @brief The node type
*/
node_type type;
/**
* @brief The location + extension of the node
*/
db::DBox location;
/**
* @brief An index locating the node in the vertex or polygon port lists
*
* For internal nodes, the index is a unique numbers.
*/
unsigned int port_index;
/**
* @brief Gets the R elements connected to this node
*/
const std::list<const RElement *> &elements () const
{
return m_elements;
}
/**
* @brief Returns a string representation of the node
*/
std::string to_string () const;
/**
* @brief Gets the network the node lives in
*/
RNetwork *graph () const
{
return mp_network;
}
protected:
friend class RNetwork;
friend class RElement;
friend class tl::list_impl<RNode, false>;
RNode (RNetwork *network, node_type _type, const db::DBox &_location, unsigned int _port_index)
: type (_type), location (_location), port_index (_port_index), mp_network (network)
{ }
~RNode () { }
private:
RNode (const RNode &other);
RNode &operator= (const RNode &other);
RNetwork *mp_network;
mutable std::list<const RElement *> m_elements;
};
/**
* @brief Represents an R element in the graph (an edge)
*
* An element has two nodes that form the ends of the edge and
* a conductance value (given in Siemens).
*
* The value can be RElement::short_value() indicating
* "infinite" conductance (a short).
*
* RElement objects cannot be created directly. Use "create_element"
* from RNetwork.
*/
struct PEX_PUBLIC RElement
: public tl::list_node<RElement>
{
/**
* @brief The conductance value
*/
double conductance;
/**
* @brief The nodes the resistor connects
*/
const RNode *a () const { return mp_a; }
const RNode *b () const { return mp_b; }
/**
* @brief Gets the other node for n
*/
const RNode *other (const RNode *n) const
{
if (mp_a == n) {
return mp_b;
} else if (mp_b == n) {
return mp_a;
}
tl_assert (false);
}
/**
* @brief Represents the conductance value for a short
*/
static double short_value ()
{
return std::numeric_limits<double>::infinity ();
}
/**
* @brief Gets the resistance value
*
* The resistance value is the inverse of the conducance.
*/
double resistance () const
{
return conductance == short_value () ? 0.0 : 1.0 / conductance;
}
/**
* @brief Returns a string representation of the element
*/
std::string to_string () const;
/**
* @brief Gets the network the node lives in
*/
RNetwork *graph () const
{
return mp_network;
}
protected:
friend class RNetwork;
friend class tl::list_impl<RElement, false>;
RElement (RNetwork *network, double _conductivity, const RNode *a, const RNode *b)
: conductance (_conductivity), mp_network (network), mp_a (a), mp_b (b)
{ }
~RElement ()
{
if (mp_a) {
mp_a->m_elements.erase (m_ia);
}
if (mp_b) {
mp_b->m_elements.erase (m_ib);
}
mp_a = mp_b = 0;
}
std::list<const RElement *>::iterator m_ia, m_ib;
RNetwork *mp_network;
const RNode *mp_a, *mp_b;
private:
RElement (const RElement &other);
RElement &operator= (const RElement &other);
};
/**
* @brief Represents a R network (a graph of RNode and RElement)
*/
class PEX_PUBLIC RNetwork
: public tl::Object
{
public:
typedef tl::list<RNode, false> node_list;
typedef node_list::const_iterator node_iterator;
typedef tl::list<RElement, false> element_list;
typedef element_list::const_iterator element_iterator;
/**
* @brief Constructor
*/
RNetwork ();
/**
* @brief Destructor
*/
~RNetwork ();
/**
* @brief Creates a node with the given type and port index
*
* If the node type is Internal, a new node is created always.
* If the node type is VertexPort or PolygonPort, an existing
* node is returned if one way created with the same type
* or port index already. This avoids creating duplicates
* for the same port.
*/
RNode *create_node (RNode::node_type type, unsigned int port_index);
/**
* @brief Creates a new element between the given nodes
*
* If an element already exists between the specified nodes, the
* given value is added to the existing element and the existing
* object is returned.
*/
RElement *create_element (double conductance, RNode *a, RNode *b);
/**
* @brief Removes the given element
*
* Removing the element will also remove any orphan nodes
* at the ends if they are of type Internal.
*/
void remove_element (RElement *element);
/**
* @brief Removes the node and the attached elements.
*
* Only nodes of type Internal can be removed.
*/
void remove_node (RNode *node);
/**
* @brief Clears the network
*/
void clear ();
/**
* @brief Simplifies the network
*
* This will:
* - Join serial resistors if connected by an internal node
* - Remove shorts and join the nodes, if one of them is
* an internal node. The non-internal node will persist.
* - Remove "dangling" resistors if the dangling node is
* an internal one
*/
void simplify ();
/**
* @brief Iterate the nodes (begin)
*/
node_iterator begin_nodes () const
{
return m_nodes.begin ();
}
/**
* @brief Iterate the nodes (end)
*/
node_iterator end_nodes () const
{
return m_nodes.end ();
}
/**
* @brief Gets the number of nodes
*/
size_t num_nodes () const
{
return m_nodes.size ();
}
/**
* @brief Gets the number of internal nodes
*/
size_t num_internal_nodes () const
{
size_t count = 0;
for (auto n = m_nodes.begin (); n != m_nodes.end (); ++n) {
if (n->type == pex::RNode::Internal) {
++count;
}
}
return count;
}
/**
* @brief Iterate the elements (begin)
*/
element_iterator begin_elements () const
{
return m_elements.begin ();
}
/**
* @brief Iterate the elements (end)
*/
element_iterator end_elements () const
{
return m_elements.end ();
}
/**
* @brief Gets the number of elements
*/
size_t num_elements () const
{
return m_elements.size ();
}
/**
* @brief Returns a string representation of the graph
*/
std::string to_string () const;
private:
node_list m_nodes;
element_list m_elements;
std::map<std::pair<RNode *, RNode *>, RElement *> m_elements_by_nodes;
std::map<std::pair<RNode::node_type, unsigned int>, RNode *> m_nodes_by_type;
RNetwork (const RNetwork &);
RNetwork &operator= (const RNetwork &);
void join_nodes (RNode *a, RNode *b);
};
/**
* @brief A base class for an resistance extractor
*

View File

@ -0,0 +1,29 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2025 Matthias Koefferlein
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 2 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, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "pexRExtractorTech.h"
namespace pex
{
// .. nothing yet ..
}

View File

@ -0,0 +1,167 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2025 Matthias Koefferlein
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 2 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, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef HDR_pexRExtractorTech
#define HDR_pexRExtractorTech
#include "pexCommon.h"
#include <list>
namespace pex
{
/**
* @brief Specifies the extraction parameters for vias
*
* Note that the layers are generic IDs. These are usigned ints specifying
* a layer.
*/
class RExtractorTechVia
{
public:
RExtractorTechVia ()
: cut_layer (0), top_conductor (0), bottom_conductor (0), resistance (0.0), merge_distance (0.0)
{
// .. nothing yet ..
}
/**
* @brief Specifies the cut layer
* This is the layer the via sits on
*/
unsigned int cut_layer;
/**
* @brief Specifies the top conductor
* The value is the ID of the top conductor layer
*/
unsigned int top_conductor;
/**
* @brief Specifies the bottom conductor
* The value is the ID of the bottom conductor layer
*/
unsigned int bottom_conductor;
/**
* @brief Specifies the resistance in Ohm * sqaure micrometer
*/
double resistance;
/**
* @brief Specifies the merge distance in micrometers
* The merge distance indicates a range under which vias are merged
* into bigger effective areas to reduce the complexity of via arrays.
*/
double merge_distance;
};
/**
* @brief Specifies the extraction parameters for a conductor layer
*
* Note that the layers are generic IDs. These are usigned ints specifying
* a layer.
*/
class RExtractorTechConductor
{
public:
/**
* @brief A algorithm to use
*/
enum Algorithm
{
/**
* @brief The square counting algorithm
* This algorithm is suitable for "long and thin" wires.
*/
SquareCounting = 0,
/**
* @brief The triangulation algorithm
* This algorithm is suitable to "large" sheets, specifically substrate.
*/
Triangulation = 1
};
/**
* @brief The constructor
*/
RExtractorTechConductor ()
: layer (0), resistance (0.0), algorithm (SquareCounting), triangulation_min_b (-1.0), triangulation_max_area (-1.0)
{
// .. nothing yet ..
}
/**
* @brief Specifies the layer
* The value is the generic ID of the layer.
*/
unsigned int layer;
/**
* @brief Specifies the sheet resistance
* The sheet resistance is given in units of Ohm / square
*/
double resistance;
/**
* @brief The algorihm to use
*/
Algorithm algorithm;
/**
* @brief The "min_b" parameter for the triangulation
* The "b" parameter is a ratio of shortest triangle edge to circle radius.
* If a negative value is given, the default value is taken.
*/
double triangulation_min_b;
/**
* @brief The "max_area" parameter for the triangulation
* The "max_area" specifies the maximum area of the triangles produced in square micrometers.
* If a negative value is given, the default value is taken.
*/
double triangulation_max_area;
};
/**
* @brief Specifies the extraction parameters
*/
class RExtractorTech
{
public:
/**
* @brief A list of via definitions
*/
std::list<RExtractorTechVia> vias;
/**
* @brief A list of conductor definitions
*/
std::list<RExtractorTechConductor> conductors;
};
}
#endif

View File

@ -0,0 +1,465 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2025 Matthias Koefferlein
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 2 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, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "pexCommon.h"
#include "pexRNetExtractor.h"
#include "pexRNetwork.h"
#include "pexRExtractorTech.h"
#include "pexSquareCountingRExtractor.h"
#include "pexTriangulationRExtractor.h"
#include "dbBoxScanner.h"
#include "dbPolygonTools.h"
#include "dbRegionProcessors.h"
#include "dbCompoundOperation.h"
#include "dbPolygonNeighborhood.h"
namespace pex
{
RNetExtractor::RNetExtractor (double dbu)
: m_dbu (dbu)
{
// .. nothing yet ..
}
void
RNetExtractor::extract (const RExtractorTech &tech,
const std::map<unsigned int, db::Region> &geo,
const std::map<unsigned int, std::vector<db::Point> > &vertex_ports,
const std::map<unsigned int, std::vector<db::Polygon> > &polygon_ports,
RNetwork &rnetwork)
{
rnetwork.clear ();
std::map<unsigned int, std::vector<ViaPort> > via_ports;
create_via_ports (tech, geo, via_ports, rnetwork);
for (auto g = geo.begin (); g != geo.end (); ++g) {
// Find the conductor spec for the given layer
const RExtractorTechConductor *cond = 0;
for (auto c = tech.conductors.begin (); c != tech.conductors.end () && !cond; ++c) {
if (c->layer == g->first) {
cond = c.operator-> ();
}
}
if (! cond) {
continue;
}
// Compute the offsets for the port indexes
// The port indexes are assigned incrementally, so the first index of a port
// for layer n is:
// size(ports for layer 0) + ... + size(ports for layer n-1)
// (size is 0 for empty port list)
unsigned int vp_offset = 0, pp_offset = 0;
for (auto p = vertex_ports.begin (); p != vertex_ports.end () && p->first < g->first; ++p) {
vp_offset += p->second.size ();
}
for (auto p = polygon_ports.begin (); p != polygon_ports.end () && p->first < g->first; ++p) {
vp_offset += p->second.size ();
}
// fetch the port list for vertex ports
auto ivp = vertex_ports.find (g->first);
static std::vector<db::Point> empty_vertex_ports;
const std::vector<db::Point> &vp = ivp == vertex_ports.end () ? empty_vertex_ports : ivp->second;
// fetch the port list for polygon ports
auto ipp = polygon_ports.find (g->first);
static std::vector<db::Polygon> empty_polygon_ports;
const std::vector<db::Polygon> &pp = ipp == polygon_ports.end () ? empty_polygon_ports : ipp->second;
// fetch the port list for via ports
auto iviap = via_ports.find (g->first);
static std::vector<ViaPort> empty_via_ports;
const std::vector<ViaPort> &viap = iviap == via_ports.end () ? empty_via_ports : iviap->second;
// extract the conductor polygon and integrate the results into the target network
extract_conductor (*cond, g->second, vp, vp_offset, pp, pp_offset, viap, rnetwork);
}
}
static double
via_conductance (const RExtractorTechVia &via_tech,
const db::Polygon &poly,
double dbu)
{
if (via_tech.resistance < 1e-10) {
return RElement::short_value ();
} else {
return (1.0 / via_tech.resistance) * dbu * dbu * poly.area ();
}
}
namespace
{
class ViaAggregationVisitor
: public db::PolygonNeighborhoodVisitor
{
public:
ViaAggregationVisitor (const RExtractorTechVia *via_tech, std::vector<std::pair<double, db::Point> > *conductances, double dbu)
: mp_via_tech (via_tech), mp_conductances (conductances), m_dbu (dbu)
{
// .. nothing yet ..
}
virtual void neighbors (const db::Layout * /*layout*/, const db::Cell * /*cell*/, const db::PolygonWithProperties &polygon, const neighbors_type &neighbors)
{
auto i = neighbors.find ((unsigned int) 1);
if (i == neighbors.end ()) {
return;
}
double c = 0;
for (auto vp = i->second.begin (); vp != i->second.end (); ++vp) {
double cc = via_conductance (*mp_via_tech, *vp, m_dbu);
if (cc == RElement::short_value ()) {
c = cc;
break;
} else {
c += cc;
}
}
mp_conductances->push_back (std::make_pair (c, polygon.box ().center ()));
}
private:
const RExtractorTechVia *mp_via_tech;
std::vector<std::pair<double, db::Point> > *mp_conductances;
double m_dbu;
};
}
void
RNetExtractor::create_via_ports (const RExtractorTech &tech,
const std::map<unsigned int, db::Region> &geo,
std::map<unsigned int, std::vector<ViaPort> > &vias,
RNetwork &rnetwork)
{
std::vector<std::pair<double, db::Point> > via_conductances;
for (auto v = tech.vias.begin (); v != tech.vias.end (); ++v) {
auto g = geo.find (v->cut_layer);
if (g == geo.end ()) {
continue;
}
via_conductances.clear ();
if (v->merge_distance > db::epsilon) {
// with merge, follow this scheme:
// 1.) do a merge by over/undersize
// 2.) do a convex decomposition, so we get convex via shapes with the bbox center inside the polygon
// 3.) re-aggregate the original via polygons and collect the total conductance per merged shape
db::Coord sz = db::coord_traits<db::Coord>::rounded (0.5 * v->merge_distance / m_dbu);
db::Region merged_vias = g->second.sized (sz).sized (-sz);
merged_vias.process (db::ConvexDecomposition (db::PO_any));
std::vector<db::CompoundRegionOperationNode *> children;
children.push_back (new db::CompoundRegionOperationPrimaryNode ());
children.push_back (new db::CompoundRegionOperationSecondaryNode (const_cast<db::Region *> (&g->second)));
ViaAggregationVisitor visitor (v.operator-> (), &via_conductances, m_dbu);
db::PolygonNeighborhoodCompoundOperationNode en_node (children, &visitor, 0);
merged_vias.cop_to_region (en_node);
} else {
for (auto p = g->second.begin_merged (); ! p.at_end (); ++p) {
via_conductances.push_back (std::make_pair (via_conductance (*v, *p, m_dbu), p->box ().center ()));
}
}
// create the via resistor elements
unsigned int port_index = 0;
for (auto vc = via_conductances.begin (); vc != via_conductances.end (); ++vc) {
RNode *a = rnetwork.create_node (RNode::Internal, port_index++);
RNode *b = rnetwork.create_node (RNode::Internal, port_index++);
rnetwork.create_element (vc->first, a, b);
vias[v->bottom_conductor].push_back (ViaPort (vc->second, a));
vias[v->top_conductor].push_back (ViaPort (vc->second, b));
}
}
}
static inline size_t make_id (size_t index, unsigned int type)
{
return (index << 2) + type;
}
static inline size_t index_from_id (size_t id)
{
return id >> 2;
}
static inline unsigned int type_from_id (size_t id)
{
return id & 3;
}
namespace
{
class ExtractingReceiver
: public db::box_scanner_receiver2<db::Polygon, size_t, db::Box, size_t>
{
public:
ExtractingReceiver (const RExtractorTechConductor *cond,
const std::vector<db::Point> *vertex_ports,
unsigned int vertex_port_index_offset,
const std::vector<db::Polygon> *polygon_ports,
unsigned int polygon_port_index_offset,
const std::vector<RNetExtractor::ViaPort> *via_ports,
double dbu,
RNetwork *rnetwork)
: mp_cond (cond),
mp_vertex_ports (vertex_ports),
mp_polygon_ports (polygon_ports),
mp_via_ports (via_ports),
m_next_internal_port_index (0),
m_vertex_port_index_offset (vertex_port_index_offset),
m_polygon_port_index_offset (polygon_port_index_offset),
m_dbu (dbu),
mp_rnetwork (rnetwork)
{
for (auto n = rnetwork->begin_nodes (); n != rnetwork->end_nodes (); ++n) {
if (n->type == RNode::Internal && n->port_index > m_next_internal_port_index) {
m_next_internal_port_index = n->port_index;
}
}
}
void finish1 (const db::Polygon *poly, const size_t poly_id)
{
auto i = m_interacting_ports.find (poly_id);
if (i == m_interacting_ports.end ()) {
static std::set<size_t> empty_ids;
extract (*poly, empty_ids);
} else {
extract (*poly, i->second);
m_interacting_ports.erase (i);
}
}
void add (const db::Polygon *poly, const size_t poly_id, const db::Box *port, const size_t port_id)
{
if (db::interact (*poly, *port)) {
m_interacting_ports[poly_id].insert (port_id);
}
}
private:
std::map<size_t, std::set<size_t> > m_interacting_ports;
const RExtractorTechConductor *mp_cond;
const std::vector<db::Point> *mp_vertex_ports;
const std::vector<db::Polygon> *mp_polygon_ports;
const std::vector<RNetExtractor::ViaPort> *mp_via_ports;
std::map<size_t, RNode *> m_id_to_node;
unsigned int m_next_internal_port_index;
unsigned int m_vertex_port_index_offset;
unsigned int m_polygon_port_index_offset;
double m_dbu;
RNetwork *mp_rnetwork;
void extract (const db::Polygon &poly, const std::set<size_t> &port_ids)
{
std::vector<db::Point> local_vertex_ports;
std::vector<size_t> local_vertex_port_ids;
std::vector<db::Polygon> local_polygon_ports;
std::vector<size_t> local_polygon_port_ids;
for (auto i = port_ids.begin (); i != port_ids.end (); ++i) {
switch (type_from_id (*i)) {
case 0: // vertex port
local_vertex_port_ids.push_back (*i);
local_vertex_ports.push_back ((*mp_vertex_ports) [index_from_id (*i)]);
break;
case 1: // via port
local_vertex_port_ids.push_back (*i);
local_vertex_ports.push_back ((*mp_via_ports) [index_from_id (*i)].position);
break;
case 2: // polygon port
local_polygon_port_ids.push_back (*i);
local_polygon_ports.push_back ((*mp_polygon_ports) [index_from_id (*i)]);
break;
}
}
pex::RNetwork local_network;
switch (mp_cond->algorithm) {
case RExtractorTechConductor::SquareCounting:
default:
{
pex::SquareCountingRExtractor rex (m_dbu);
rex.extract (poly, local_vertex_ports, local_polygon_ports, local_network);
}
break;
case RExtractorTechConductor::Triangulation:
{
pex::TriangulationRExtractor rex (m_dbu);
rex.extract (poly, local_vertex_ports, local_polygon_ports, local_network);
}
break;
}
integrate (local_network, local_vertex_port_ids, local_polygon_port_ids);
}
void integrate (const RNetwork &local_network,
const std::vector<size_t> &local_vertex_port_ids,
const std::vector<size_t> &local_polygon_port_ids)
{
// create or find the new nodes in the target network
std::unordered_map<const RNode *, RNode *> n2n;
for (auto n = local_network.begin_nodes (); n != local_network.end_nodes (); ++n) {
const RNode *local = n.operator-> ();
RNode *global = 0;
if (local->type == RNode::Internal) {
// for internal nodes always create a node in the target network
global = mp_rnetwork->create_node (local->type, ++m_next_internal_port_index);
} else if (local->type == RNode::VertexPort) {
// for vertex nodes reuse the via node or create a new target node, unless one
// was created already.
size_t id = local_vertex_port_ids [local->port_index];
auto i2n = m_id_to_node.find (id);
if (i2n != m_id_to_node.end ()) {
global = i2n->second;
} else {
if (type_from_id (id) == 0) { // vertex port
global = mp_rnetwork->create_node (RNode::VertexPort, index_from_id (id) + m_vertex_port_index_offset);
global->location = local->location;
} else if (type_from_id (id) == 1) { // via port
global = (*mp_via_ports) [index_from_id (id)].node;
}
m_id_to_node.insert (std::make_pair (id, global));
}
} else if (local->type == RNode::PolygonPort) {
// for polygon nodes create a new target node, unless one was created already.
size_t id = local_polygon_port_ids [local->port_index];
tl_assert (type_from_id (id) == 2);
auto i2n = m_id_to_node.find (id);
if (i2n != m_id_to_node.end ()) {
global = i2n->second;
} else {
global = mp_rnetwork->create_node (RNode::VertexPort, index_from_id (id) + m_polygon_port_index_offset);
global->location = local->location;
m_id_to_node.insert (std::make_pair (id, global));
}
}
tl_assert (global != 0);
n2n.insert (std::make_pair (local, global));
}
// create the R elements in the target network
for (auto e = local_network.begin_elements (); e != local_network.begin_elements (); ++e) {
const RElement *local = e.operator-> ();
auto ia = n2n.find (local->a ());
auto ib = n2n.find (local->b ());
tl_assert (ia != n2n.end ());
tl_assert (ia != n2n.end ());
mp_rnetwork->create_element (local->conductance, ia->second, ib->second);
}
}
};
}
void
RNetExtractor::extract_conductor (const RExtractorTechConductor &cond,
const db::Region &region,
const std::vector<db::Point> &vertex_ports,
unsigned int vertex_ports_index_offset,
const std::vector<db::Polygon> &polygon_ports,
unsigned int polygon_ports_index_offset,
const std::vector<ViaPort> &via_ports,
RNetwork &rnetwork)
{
db::box_scanner2<db::Polygon, size_t, db::Box, size_t> scanner;
size_t poly_id = 0;
for (auto p = region.addressable_merged_polygons (); ! p.at_end (); ++p) {
scanner.insert1 (p.operator-> (), poly_id++);
}
std::list<db::Box> box_heap;
// type 0 objects (vertex ports)
for (auto i = vertex_ports.begin (); i != vertex_ports.end (); ++i) {
// @@@ could be without enlarge?
box_heap.push_back (db::Box (*i, *i).enlarged (db::Vector (1, 1)));
scanner.insert2 (&box_heap.back (), make_id (i - vertex_ports.begin (), 0));
}
// type 1 objects (via ports)
for (auto i = via_ports.begin (); i != via_ports.end (); ++i) {
// @@@ could be without enlarge?
box_heap.push_back (db::Box (i->position, i->position).enlarged (db::Vector (1, 1)));
scanner.insert2 (&box_heap.back (), make_id (i - via_ports.begin (), 1));
}
// type 2 objects (polygon ports)
for (auto i = polygon_ports.begin (); i != polygon_ports.end (); ++i) {
box_heap.push_back (i->box ());
scanner.insert2 (&box_heap.back (), make_id (i - polygon_ports.begin (), 2));
}
ExtractingReceiver rec (&cond, &vertex_ports, vertex_ports_index_offset, &polygon_ports, polygon_ports_index_offset, &via_ports, m_dbu, &rnetwork);
scanner.process (rec, 0, db::box_convert<db::Polygon> (), db::box_convert<db::Box> ());
}
}

View File

@ -0,0 +1,97 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2025 Matthias Koefferlein
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 2 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, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef HDR_pexRNetExtractor
#define HDR_pexRNetExtractor
#include "pexCommon.h"
#include "dbRegion.h"
namespace pex
{
class RExtractorTech;
class RExtractorTechConductor;
class RNetwork;
class RNode;
/**
* @brief Implementation of the R extractor for a multi-polygon/multi-layer net
*/
class RNetExtractor
{
public:
/**
* @brief Constructor
* @param dbu The database unit to be used to convert coordinates into micrometers
*/
RNetExtractor (double dbu);
/**
* @brief Extracts a R network from a given set of geometries and ports
* @param geo The geometries per layer
* @param vertex_ports The vertex ports - a list of layer and points to attache a port to on this layer
* @param polygon_ports The polygon ports - a list of layer and polygons to attach a port to on this layer
* @param rnetwork The network extracted (output)
*
* The network nodes will carry the information about the port, in case they
* have been generated from a port.
*/
void extract (const RExtractorTech &tech,
const std::map<unsigned int, db::Region> &geo,
const std::map<unsigned int, std::vector<db::Point> > &vertex_ports,
const std::map<unsigned int, std::vector<db::Polygon> > &polygon_ports,
RNetwork &rnetwork);
/**
* @brief A structure describing a via port
* This structure is used internally
*/
struct ViaPort
{
ViaPort () : node (0) { }
ViaPort (const db::Point &p, RNode *n) : position (p), node (n) { }
db::Point position;
RNode *node;
};
private:
double m_dbu;
void create_via_ports (const RExtractorTech &tech,
const std::map<unsigned int, db::Region> &geo,
std::map<unsigned int, std::vector<ViaPort> > &vias,
RNetwork &rnetwork);
void extract_conductor (const RExtractorTechConductor &cond,
const db::Region &region,
const std::vector<db::Point> &vertex_ports,
unsigned int vertex_ports_index_offset,
const std::vector<db::Polygon> &polygon_ports,
unsigned int polygon_ports_index_offset,
const std::vector<ViaPort> &via_ports,
RNetwork &rnetwork);
};
}
#endif

309
src/pex/pex/pexRNetwork.cc Normal file
View File

@ -0,0 +1,309 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2025 Matthias Koefferlein
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 2 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, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "pexRNetwork.h"
#include "tlEquivalenceClusters.h"
namespace pex
{
// -----------------------------------------------------------------------------
std::string
RNode::to_string () const
{
std::string res;
switch (type) {
default:
res += "$" + tl::to_string (port_index);
break;
case VertexPort:
res += "V" + tl::to_string (port_index);
break;
case PolygonPort:
res += "P" + tl::to_string (port_index);
break;
}
return res;
}
// -----------------------------------------------------------------------------
std::string
RElement::to_string () const
{
std::string na;
if (a ()) {
na = a ()->to_string ();
} else {
na = "(nil)";
}
std::string nb;
if (b ()) {
nb = b ()->to_string ();
} else {
nb = "(nil)";
}
if (nb < na) {
std::swap (na, nb);
}
std::string res = "R " + na + " " + nb + " ";
res += tl::sprintf ("%.6g", resistance ());
return res;
}
// -----------------------------------------------------------------------------
RNetwork::RNetwork ()
{
// .. nothing yet ..
}
RNetwork::~RNetwork ()
{
clear ();
}
std::string
RNetwork::to_string () const
{
std::string res;
for (auto e = m_elements.begin (); e != m_elements.end (); ++e) {
if (! res.empty ()) {
res += "\n";
}
res += e->to_string ();
}
return res;
}
void
RNetwork::clear ()
{
m_elements.clear (); // must happen before m_nodes
m_nodes.clear ();
m_elements_by_nodes.clear ();
m_nodes_by_type.clear ();
}
RNode *
RNetwork::create_node (RNode::node_type type, unsigned int port_index)
{
if (type != RNode::Internal) {
auto i = m_nodes_by_type.find (std::make_pair (type, port_index));
if (i != m_nodes_by_type.end ()) {
return i->second;
} else {
RNode *new_node = new RNode (this, type, db::DBox (), port_index);
m_nodes.push_back (new_node);
m_nodes_by_type.insert (std::make_pair (std::make_pair (type, port_index), new_node));
return new_node;
}
} else {
RNode *new_node = new RNode (this, type, db::DBox (), port_index);
m_nodes.push_back (new_node);
return new_node;
}
}
RElement *
RNetwork::create_element (double conductance, RNode *a, RNode *b)
{
std::pair<RNode *, RNode *> key (a, b);
if (size_t (b) < size_t (a)) {
std::swap (key.first, key.second);
}
auto i = m_elements_by_nodes.find (key);
if (i != m_elements_by_nodes.end ()) {
if (conductance == pex::RElement::short_value () || i->second->conductance == pex::RElement::short_value ()) {
i->second->conductance = pex::RElement::short_value ();
} else {
i->second->conductance += conductance;
}
return i->second;
} else {
RElement *element = new RElement (this, conductance, a, b);
m_elements.push_back (element);
m_elements_by_nodes.insert (std::make_pair (key, element));
a->m_elements.push_back (element);
element->m_ia = --a->m_elements.end ();
b->m_elements.push_back (element);
element->m_ib = --b->m_elements.end ();
return element;
}
}
void
RNetwork::remove_node (RNode *node)
{
tl_assert (node->type == RNode::Internal);
while (! node->m_elements.empty ()) {
delete const_cast<RElement *> (node->m_elements.front ());
}
delete node;
}
void
RNetwork::remove_element (RElement *element)
{
RNode *a = const_cast<RNode *> (element->a ());
RNode *b = const_cast<RNode *> (element->b ());
delete element;
if (a && a->type == RNode::Internal && a->m_elements.empty ()) {
delete a;
}
if (b && b->type == RNode::Internal && b->m_elements.empty ()) {
delete b;
}
}
void
RNetwork::join_nodes (RNode *a, RNode *b)
{
for (auto e = b->elements ().begin (); e != b->elements ().end (); ++e) {
RNode *on = const_cast<RNode *> ((*e)->other (b));
if (on != a) {
create_element ((*e)->conductance, on, a);
}
}
a->location += b->location;
remove_node (b);
}
void
RNetwork::simplify ()
{
bool any_change = true;
while (any_change) {
any_change = false;
// join shorted clusters - we take care to remove internal nodes only
tl::equivalence_clusters<const RNode *> clusters;
for (auto e = m_elements.begin (); e != m_elements.end (); ++e) {
if (e->conductance == pex::RElement::short_value () && (e->a ()->type == pex::RNode::Internal || e->b ()->type == pex::RNode::Internal)) {
clusters.same (e->a (), e->b ());
}
}
for (size_t ic = 1; ic <= clusters.size (); ++ic) {
RNode *remaining = 0;
RNode *first_node = 0;
for (auto c = clusters.begin_cluster (ic); c != clusters.end_cluster (ic); ++c) {
RNode *n = const_cast<RNode *> ((*c)->first);
if (! first_node) {
first_node = n;
}
if (n->type != pex::RNode::Internal) {
remaining = n;
break;
}
}
if (! remaining) {
// Only internal nodes
remaining = first_node;
}
for (auto c = clusters.begin_cluster (ic); c != clusters.end_cluster (ic); ++c) {
RNode *n = const_cast<RNode *> ((*c)->first);
if (n != remaining && n->type == pex::RNode::Internal) {
any_change = true;
join_nodes (remaining, n);
}
}
}
// combine serial resistors if connected through an internal node
std::vector<RNode *> nodes_to_remove;
for (auto n = m_nodes.begin (); n != m_nodes.end (); ++n) {
size_t nres = n->elements ().size ();
if (n->type == pex::RNode::Internal && nres <= 2) {
any_change = true;
if (nres == 2) {
auto e = n->elements ().begin ();
RNode *n1 = const_cast<RNode *> ((*e)->other (n.operator-> ()));
double r1 = (*e)->resistance ();
++e;
RNode *n2 = const_cast<RNode *> ((*e)->other (n.operator-> ()));
double r2 = (*e)->resistance ();
double r = r1 + r2;
if (r == 0.0) {
create_element (pex::RElement::short_value (), n1, n2);
} else {
create_element (1.0 / r, n1, n2);
}
}
nodes_to_remove.push_back (n.operator-> ());
}
}
for (auto n = nodes_to_remove.begin (); n != nodes_to_remove.end (); ++n) {
remove_node (*n);
}
}
}
}

377
src/pex/pex/pexRNetwork.h Normal file
View File

@ -0,0 +1,377 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2025 Matthias Koefferlein
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 2 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, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef HDR_pexRNetwork
#define HDR_pexRNetwork
#include "pexCommon.h"
#include "dbPolygon.h"
#include "dbPLC.h"
#include "tlList.h"
#include <string>
#include <list>
#include <limits>
namespace pex
{
class RElement;
class RNode;
class RNetwork;
/**
* @brief Represents a node in the R graph
*
* A node connects to multiple elements (resistors).
* Every element has two nodes. The nodes and elements form
* a graph.
*
* RNode object cannot be created directly. Use "create_node"
* from RNetwork.
*/
struct PEX_PUBLIC RNode
: public tl::list_node<RNode>
{
public:
/**
* @brief The type of the node
*/
enum node_type {
Internal, // an internal node, not related to a port
VertexPort, // a node related to a vertex port
PolygonPort // a node related to a polygon port
};
/**
* @brief The node type
*/
node_type type;
/**
* @brief The location + extension of the node
*/
db::DBox location;
/**
* @brief An index locating the node in the vertex or polygon port lists
*
* For internal nodes, the index is a unique numbers.
*/
unsigned int port_index;
/**
* @brief Gets the R elements connected to this node
*/
const std::list<const RElement *> &elements () const
{
return m_elements;
}
/**
* @brief Returns a string representation of the node
*/
std::string to_string () const;
/**
* @brief Gets the network the node lives in
*/
RNetwork *graph () const
{
return mp_network;
}
protected:
friend class RNetwork;
friend class RElement;
friend class tl::list_impl<RNode, false>;
RNode (RNetwork *network, node_type _type, const db::DBox &_location, unsigned int _port_index)
: type (_type), location (_location), port_index (_port_index), mp_network (network)
{ }
~RNode () { }
private:
RNode (const RNode &other);
RNode &operator= (const RNode &other);
RNetwork *mp_network;
mutable std::list<const RElement *> m_elements;
};
/**
* @brief Represents an R element in the graph (an edge)
*
* An element has two nodes that form the ends of the edge and
* a conductance value (given in Siemens).
*
* The value can be RElement::short_value() indicating
* "infinite" conductance (a short).
*
* RElement objects cannot be created directly. Use "create_element"
* from RNetwork.
*/
struct PEX_PUBLIC RElement
: public tl::list_node<RElement>
{
/**
* @brief The conductance value
*/
double conductance;
/**
* @brief The nodes the resistor connects
*/
const RNode *a () const { return mp_a; }
const RNode *b () const { return mp_b; }
/**
* @brief Gets the other node for n
*/
const RNode *other (const RNode *n) const
{
if (mp_a == n) {
return mp_b;
} else if (mp_b == n) {
return mp_a;
}
tl_assert (false);
}
/**
* @brief Represents the conductance value for a short
*/
static double short_value ()
{
return std::numeric_limits<double>::infinity ();
}
/**
* @brief Gets the resistance value
*
* The resistance value is the inverse of the conducance.
*/
double resistance () const
{
return conductance == short_value () ? 0.0 : 1.0 / conductance;
}
/**
* @brief Returns a string representation of the element
*/
std::string to_string () const;
/**
* @brief Gets the network the node lives in
*/
RNetwork *graph () const
{
return mp_network;
}
protected:
friend class RNetwork;
friend class tl::list_impl<RElement, false>;
RElement (RNetwork *network, double _conductivity, const RNode *a, const RNode *b)
: conductance (_conductivity), mp_network (network), mp_a (a), mp_b (b)
{ }
~RElement ()
{
if (mp_a) {
mp_a->m_elements.erase (m_ia);
}
if (mp_b) {
mp_b->m_elements.erase (m_ib);
}
mp_a = mp_b = 0;
}
std::list<const RElement *>::iterator m_ia, m_ib;
RNetwork *mp_network;
const RNode *mp_a, *mp_b;
private:
RElement (const RElement &other);
RElement &operator= (const RElement &other);
};
/**
* @brief Represents a R network (a graph of RNode and RElement)
*/
class PEX_PUBLIC RNetwork
: public tl::Object
{
public:
typedef tl::list<RNode, false> node_list;
typedef node_list::const_iterator node_iterator;
typedef tl::list<RElement, false> element_list;
typedef element_list::const_iterator element_iterator;
/**
* @brief Constructor
*/
RNetwork ();
/**
* @brief Destructor
*/
~RNetwork ();
/**
* @brief Creates a node with the given type and port index
*
* If the node type is Internal, a new node is created always.
* If the node type is VertexPort or PolygonPort, an existing
* node is returned if one way created with the same type
* or port index already. This avoids creating duplicates
* for the same port.
*/
RNode *create_node (RNode::node_type type, unsigned int port_index);
/**
* @brief Creates a new element between the given nodes
*
* If an element already exists between the specified nodes, the
* given value is added to the existing element and the existing
* object is returned.
*/
RElement *create_element (double conductance, RNode *a, RNode *b);
/**
* @brief Removes the given element
*
* Removing the element will also remove any orphan nodes
* at the ends if they are of type Internal.
*/
void remove_element (RElement *element);
/**
* @brief Removes the node and the attached elements.
*
* Only nodes of type Internal can be removed.
*/
void remove_node (RNode *node);
/**
* @brief Clears the network
*/
void clear ();
/**
* @brief Simplifies the network
*
* This will:
* - Join serial resistors if connected by an internal node
* - Remove shorts and join the nodes, if one of them is
* an internal node. The non-internal node will persist.
* - Remove "dangling" resistors if the dangling node is
* an internal one
*/
void simplify ();
/**
* @brief Iterate the nodes (begin)
*/
node_iterator begin_nodes () const
{
return m_nodes.begin ();
}
/**
* @brief Iterate the nodes (end)
*/
node_iterator end_nodes () const
{
return m_nodes.end ();
}
/**
* @brief Gets the number of nodes
*/
size_t num_nodes () const
{
return m_nodes.size ();
}
/**
* @brief Gets the number of internal nodes
*/
size_t num_internal_nodes () const
{
size_t count = 0;
for (auto n = m_nodes.begin (); n != m_nodes.end (); ++n) {
if (n->type == pex::RNode::Internal) {
++count;
}
}
return count;
}
/**
* @brief Iterate the elements (begin)
*/
element_iterator begin_elements () const
{
return m_elements.begin ();
}
/**
* @brief Iterate the elements (end)
*/
element_iterator end_elements () const
{
return m_elements.end ();
}
/**
* @brief Gets the number of elements
*/
size_t num_elements () const
{
return m_elements.size ();
}
/**
* @brief Returns a string representation of the graph
*/
std::string to_string () const;
private:
node_list m_nodes;
element_list m_elements;
std::map<std::pair<RNode *, RNode *>, RElement *> m_elements_by_nodes;
std::map<std::pair<RNode::node_type, unsigned int>, RNode *> m_nodes_by_type;
RNetwork (const RNetwork &);
RNetwork &operator= (const RNetwork &);
void join_nodes (RNode *a, RNode *b);
};
}
#endif

View File

@ -24,6 +24,7 @@
#define HDR_pexSquareCountingRExtractor
#include "pexCommon.h"
#include "pexRNetwork.h"
#include "pexRExtractor.h"
#include "dbPLCConvexDecomposition.h"

View File

@ -24,6 +24,7 @@
#define HDR_pexTriangulationRExtractor
#include "pexCommon.h"
#include "pexRNetwork.h"
#include "pexRExtractor.h"
#include "dbPLCTriangulation.h"

View File

@ -22,6 +22,7 @@
#include "pexRExtractor.h"
#include "pexRNetwork.h"
#include "tlUnitTest.h"
TEST(network_basic)