mirror of https://github.com/KLayout/klayout.git
WIP: network R extractor, needs testing
This commit is contained in:
parent
f0943dea53
commit
bc10bb6b14
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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 ..
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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 ..
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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 ®ion,
|
||||
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> ());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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 ®ion,
|
||||
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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -24,6 +24,7 @@
|
|||
#define HDR_pexSquareCountingRExtractor
|
||||
|
||||
#include "pexCommon.h"
|
||||
#include "pexRNetwork.h"
|
||||
#include "pexRExtractor.h"
|
||||
|
||||
#include "dbPLCConvexDecomposition.h"
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
#define HDR_pexTriangulationRExtractor
|
||||
|
||||
#include "pexCommon.h"
|
||||
#include "pexRNetwork.h"
|
||||
#include "pexRExtractor.h"
|
||||
|
||||
#include "dbPLCTriangulation.h"
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
|
||||
#include "pexRExtractor.h"
|
||||
#include "pexRNetwork.h"
|
||||
#include "tlUnitTest.h"
|
||||
|
||||
TEST(network_basic)
|
||||
|
|
|
|||
Loading…
Reference in New Issue