From bc10bb6b14ead270f487a360b9b6623068c91639 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 3 May 2025 22:36:39 +0200 Subject: [PATCH] WIP: network R extractor, needs testing --- src/pex/pex/pex.pro | 6 + src/pex/pex/pexRExtractor.cc | 282 ------------- src/pex/pex/pexRExtractor.h | 340 ---------------- src/pex/pex/pexRExtractorTech.cc | 29 ++ src/pex/pex/pexRExtractorTech.h | 167 ++++++++ src/pex/pex/pexRNetExtractor.cc | 465 ++++++++++++++++++++++ src/pex/pex/pexRNetExtractor.h | 97 +++++ src/pex/pex/pexRNetwork.cc | 309 ++++++++++++++ src/pex/pex/pexRNetwork.h | 377 ++++++++++++++++++ src/pex/pex/pexSquareCountingRExtractor.h | 1 + src/pex/pex/pexTriangulationRExtractor.h | 1 + src/pex/unit_tests/pexRExtractorTests.cc | 1 + 12 files changed, 1453 insertions(+), 622 deletions(-) create mode 100644 src/pex/pex/pexRExtractorTech.cc create mode 100644 src/pex/pex/pexRExtractorTech.h create mode 100644 src/pex/pex/pexRNetExtractor.cc create mode 100644 src/pex/pex/pexRNetExtractor.h create mode 100644 src/pex/pex/pexRNetwork.cc create mode 100644 src/pex/pex/pexRNetwork.h diff --git a/src/pex/pex/pex.pro b/src/pex/pex/pex.pro index 145a1866e..6d69e018c 100644 --- a/src/pex/pex/pex.pro +++ b/src/pex/pex/pex.pro @@ -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 diff --git a/src/pex/pex/pexRExtractor.cc b/src/pex/pex/pexRExtractor.cc index 11c36c771..10afe62cc 100644 --- a/src/pex/pex/pexRExtractor.cc +++ b/src/pex/pex/pexRExtractor.cc @@ -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 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 (node->m_elements.front ()); - } - delete node; -} - -void -RNetwork::remove_element (RElement *element) -{ - RNode *a = const_cast (element->a ()); - RNode *b = const_cast (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 ((*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 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 ((*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 ((*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 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 ((*e)->other (n.operator-> ())); - double r1 = (*e)->resistance (); - - ++e; - RNode *n2 = const_cast ((*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 .. diff --git a/src/pex/pex/pexRExtractor.h b/src/pex/pex/pexRExtractor.h index b9da173bc..e80c3f248 100644 --- a/src/pex/pex/pexRExtractor.h +++ b/src/pex/pex/pexRExtractor.h @@ -26,352 +26,12 @@ #include "pexCommon.h" #include "dbPolygon.h" -#include "dbPLC.h" -#include "tlList.h" - -#include -#include -#include 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 -{ -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 &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 (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 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 -{ - /** - * @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::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 (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::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 node_list; - typedef node_list::const_iterator node_iterator; - typedef tl::list 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, RElement *> m_elements_by_nodes; - std::map, 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 * diff --git a/src/pex/pex/pexRExtractorTech.cc b/src/pex/pex/pexRExtractorTech.cc new file mode 100644 index 000000000..65e1a6604 --- /dev/null +++ b/src/pex/pex/pexRExtractorTech.cc @@ -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 .. +} diff --git a/src/pex/pex/pexRExtractorTech.h b/src/pex/pex/pexRExtractorTech.h new file mode 100644 index 000000000..b2840d020 --- /dev/null +++ b/src/pex/pex/pexRExtractorTech.h @@ -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 + +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 vias; + + /** + * @brief A list of conductor definitions + */ + std::list conductors; +}; + +} + +#endif + diff --git a/src/pex/pex/pexRNetExtractor.cc b/src/pex/pex/pexRNetExtractor.cc new file mode 100644 index 000000000..78f4f6a4b --- /dev/null +++ b/src/pex/pex/pexRNetExtractor.cc @@ -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 &geo, + const std::map > &vertex_ports, + const std::map > &polygon_ports, + RNetwork &rnetwork) +{ + rnetwork.clear (); + + std::map > 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 empty_vertex_ports; + const std::vector &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 empty_polygon_ports; + const std::vector &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 empty_via_ports; + const std::vector &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 > *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 > *mp_conductances; + double m_dbu; +}; + +} + +void +RNetExtractor::create_via_ports (const RExtractorTech &tech, + const std::map &geo, + std::map > &vias, + RNetwork &rnetwork) +{ + std::vector > 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::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 children; + children.push_back (new db::CompoundRegionOperationPrimaryNode ()); + children.push_back (new db::CompoundRegionOperationSecondaryNode (const_cast (&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 +{ +public: + ExtractingReceiver (const RExtractorTechConductor *cond, + const std::vector *vertex_ports, + unsigned int vertex_port_index_offset, + const std::vector *polygon_ports, + unsigned int polygon_port_index_offset, + const std::vector *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 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 > m_interacting_ports; + const RExtractorTechConductor *mp_cond; + const std::vector *mp_vertex_ports; + const std::vector *mp_polygon_ports; + const std::vector *mp_via_ports; + std::map 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 &port_ids) + { + std::vector local_vertex_ports; + std::vector local_vertex_port_ids; + std::vector local_polygon_ports; + std::vector 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 &local_vertex_port_ids, + const std::vector &local_polygon_port_ids) + { + // create or find the new nodes in the target network + std::unordered_map 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 &vertex_ports, + unsigned int vertex_ports_index_offset, + const std::vector &polygon_ports, + unsigned int polygon_ports_index_offset, + const std::vector &via_ports, + RNetwork &rnetwork) +{ + db::box_scanner2 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 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::box_convert ()); +} + +} diff --git a/src/pex/pex/pexRNetExtractor.h b/src/pex/pex/pexRNetExtractor.h new file mode 100644 index 000000000..a2df7d6aa --- /dev/null +++ b/src/pex/pex/pexRNetExtractor.h @@ -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 &geo, + const std::map > &vertex_ports, + const std::map > &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 &geo, + std::map > &vias, + RNetwork &rnetwork); + + void extract_conductor (const RExtractorTechConductor &cond, + const db::Region ®ion, + const std::vector &vertex_ports, + unsigned int vertex_ports_index_offset, + const std::vector &polygon_ports, + unsigned int polygon_ports_index_offset, + const std::vector &via_ports, + RNetwork &rnetwork); +}; + +} + +#endif diff --git a/src/pex/pex/pexRNetwork.cc b/src/pex/pex/pexRNetwork.cc new file mode 100644 index 000000000..0a552e2e2 --- /dev/null +++ b/src/pex/pex/pexRNetwork.cc @@ -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 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 (node->m_elements.front ()); + } + delete node; +} + +void +RNetwork::remove_element (RElement *element) +{ + RNode *a = const_cast (element->a ()); + RNode *b = const_cast (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 ((*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 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 ((*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 ((*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 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 ((*e)->other (n.operator-> ())); + double r1 = (*e)->resistance (); + + ++e; + RNode *n2 = const_cast ((*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); + } + + } + +} + +} diff --git a/src/pex/pex/pexRNetwork.h b/src/pex/pex/pexRNetwork.h new file mode 100644 index 000000000..d0fe65a9a --- /dev/null +++ b/src/pex/pex/pexRNetwork.h @@ -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 +#include +#include + +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 +{ +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 &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 (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 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 +{ + /** + * @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::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 (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::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 node_list; + typedef node_list::const_iterator node_iterator; + typedef tl::list 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, RElement *> m_elements_by_nodes; + std::map, RNode *> m_nodes_by_type; + + RNetwork (const RNetwork &); + RNetwork &operator= (const RNetwork &); + + void join_nodes (RNode *a, RNode *b); +}; + +} + +#endif + diff --git a/src/pex/pex/pexSquareCountingRExtractor.h b/src/pex/pex/pexSquareCountingRExtractor.h index 2d95b3e26..e5228ad87 100644 --- a/src/pex/pex/pexSquareCountingRExtractor.h +++ b/src/pex/pex/pexSquareCountingRExtractor.h @@ -24,6 +24,7 @@ #define HDR_pexSquareCountingRExtractor #include "pexCommon.h" +#include "pexRNetwork.h" #include "pexRExtractor.h" #include "dbPLCConvexDecomposition.h" diff --git a/src/pex/pex/pexTriangulationRExtractor.h b/src/pex/pex/pexTriangulationRExtractor.h index 6749c7f84..634cf81eb 100644 --- a/src/pex/pex/pexTriangulationRExtractor.h +++ b/src/pex/pex/pexTriangulationRExtractor.h @@ -24,6 +24,7 @@ #define HDR_pexTriangulationRExtractor #include "pexCommon.h" +#include "pexRNetwork.h" #include "pexRExtractor.h" #include "dbPLCTriangulation.h" diff --git a/src/pex/unit_tests/pexRExtractorTests.cc b/src/pex/unit_tests/pexRExtractorTests.cc index b0ace9056..6e04a6f6a 100644 --- a/src/pex/unit_tests/pexRExtractorTests.cc +++ b/src/pex/unit_tests/pexRExtractorTests.cc @@ -22,6 +22,7 @@ #include "pexRExtractor.h" +#include "pexRNetwork.h" #include "tlUnitTest.h" TEST(network_basic)