diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index 80407fdfd..77f33297e 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -156,8 +156,8 @@ template DB_PUBLIC bool Connectivity::interacts (const db::Polyg // local_cluster implementation template -local_cluster::local_cluster () - : m_id (0), m_needs_update (false), m_size (0) +local_cluster::local_cluster (size_t id) + : m_id (id), m_needs_update (false), m_size (0) { // .. nothing yet .. } @@ -372,9 +372,104 @@ local_cluster::interacts (const local_cluster &other, const db::ICplxTrans return ! scanner.process (rec, 1 /*==touching*/, bc, bc_t); } +template +double local_cluster::area_ratio () const +{ + box_type bx = bbox (); + if (bx.empty ()) { + return 0.0; + } + + db::box_convert bc; + + // just the sum of the areas of the bounding boxes - this is precise if no overlaps happen and the + // polygons are rather rectangular. This criterion is coarse enough to prevent recursion in the split + // algorithm and still be fine enough - consider that we a planning to use splitted polygons for + // which the bbox is a fairly good approximation. + typename box_type::area_type a = 0; + for (typename std::map::const_iterator s = m_shapes.begin (); s != m_shapes.end (); ++s) { + for (typename tree_type::const_iterator i = s->second.begin (); i != s->second.end (); ++i) { + a += bc (*i).area (); + } + } + + return (a == 0 ? 0.0 : double (bx.area ()) / double (a)); +} + +template +std::vector +local_cluster::layers () const +{ + std::vector l; + l.reserve (m_shapes.size ()); + for (typename std::map::const_iterator s = m_shapes.begin (); s != m_shapes.end (); ++s) { + l.push_back (s->first); + } + return l; +} + +template +size_t split_cluster (const local_cluster &cl, double max_area_ratio, Iter &output) +{ + if (cl.area_ratio () < max_area_ratio) { + return 0; // no splitting happened + } + + db::box_convert bc; + typename local_cluster::box_type bx = cl.bbox (); + + int xthr = bx.width () > bx.height () ? bx.center ().x () : bx.left (); + int ythr = bx.width () > bx.height () ? bx.bottom () : bx.center ().y (); + + // split along the longer axis - decide the position according to the bbox center + + local_cluster a (cl.id ()), b (cl.id ()); + + std::vector layers = cl.layers (); + + for (std::vector::const_iterator l = layers.begin (); l != layers.end (); ++l) { + for (typename local_cluster::shape_iterator s = cl.begin (*l); ! s.at_end (); ++s) { + typename local_cluster::box_type::point_type sc = bc (*s).center (); + if (sc.x () < xthr || sc.y () < ythr) { + a.add (*s, *l); + } else { + b.add (*s, *l); + } + } + } + + if (a.size () == 0 || b.size () == 0) { + // give up to prevent infinite recursion + return 0; + } + + // split further if required + size_t na = split_cluster (a, max_area_ratio, output); + size_t nb = split_cluster (b, max_area_ratio, output); + + if (na == 0) { + *output++ = a; + na = 1; + } + + if (nb == 0) { + *output++ = b; + nb = 1; + } + + return na + nb; +} + +template +template +size_t local_cluster::split (double max_area_ratio, Iter &output) const +{ + return split_cluster (*this, max_area_ratio, output); +} + // explicit instantiations template class DB_PUBLIC local_cluster; - +template DB_PUBLIC size_t local_cluster::split > > > (double, std::back_insert_iterator > > &) const; // ------------------------------------------------------------------------------ // local_clusters implementation @@ -862,23 +957,22 @@ private: return; } + db::ICplxTrans t1i = t1.inverted (); db::ICplxTrans t2i = t2.inverted (); - db::ICplxTrans t12 = t2i * t1; box_type common = b1 & b2; - for (db::CellInstArray::iterator ii1 = i1.begin_touching (common, mp_layout); ! ii1.at_end (); ++ii1) { + for (db::CellInstArray::iterator ii1 = i1.begin_touching (common.transformed (t1i), mp_layout); ! ii1.at_end (); ++ii1) { db::ICplxTrans tt1 = t1 * i1.complex_trans (*ii1); box_type ib1 = bb1.transformed (tt1); - box_type ib12 = ib1.transformed (t12); std::vector pp1; pp1.reserve (p1.size () + 1); pp1.insert (pp1.end (), p1.begin (), p1.end ()); pp1.push_back (db::InstElement (i1, ii1)); - for (db::CellInstArray::iterator ii2 = i2.begin_touching (ib12, mp_layout); ! ii2.at_end (); ++ii2) { + for (db::CellInstArray::iterator ii2 = i2.begin_touching (ib1.transformed (t2i), mp_layout); ! ii2.at_end (); ++ii2) { db::ICplxTrans tt2 = t2 * i2.complex_trans (*ii2); box_type ib2 = bb2.transformed (tt2); @@ -1392,6 +1486,9 @@ hier_clusters::build_hier_connections (cell_clusters_box_converter &cbc, c // handle local to instance connections { + std::list > heap; + double area_ratio = 10.0; + static std::string desc = tl::to_string (tr ("Local to instance treatment")); tl::SelfTimer timer (tl::verbosity () >= 51, desc); @@ -1399,8 +1496,21 @@ hier_clusters::build_hier_connections (cell_clusters_box_converter &cbc, c db::box_scanner2, unsigned int, db::Instance, unsigned int> bs2 (report_progress, desc); for (typename connected_clusters::const_iterator c = local.begin (); c != local.end (); ++c) { - bs2.insert1 (c.operator-> (), 0); + + // we do not actually need the original clusters. For a better performance we optimize the + // area ratio and split, but we keep the ID the same. + std::back_insert_iterator > > iout = std::back_inserter (heap); + size_t n = c->split (area_ratio, iout); + if (n == 0) { + bs2.insert1 (c.operator-> (), 0); + } else { + std::list >::iterator h = heap.end (); + while (n-- > 0) { + bs2.insert1 ((--h).operator-> (), 0); + } + } } + for (std::vector::const_iterator inst = inst_storage.begin (); inst != inst_storage.end (); ++inst) { bs2.insert2 (inst.operator-> (), 0); } diff --git a/src/db/db/dbHierNetworkProcessor.h b/src/db/db/dbHierNetworkProcessor.h index d65d4c5f7..e1e670b82 100644 --- a/src/db/db/dbHierNetworkProcessor.h +++ b/src/db/db/dbHierNetworkProcessor.h @@ -146,7 +146,7 @@ public: /** * @brief Creates an empty cluster */ - local_cluster (); + local_cluster (size_t id = 0); /** * @brief Clears the cluster @@ -201,9 +201,29 @@ public: return m_bbox; } + /** + * @brief Computes the "area ratio" of the cluster - this is a rough approximation of the area covered + * The algorithm used assumes no overlap between the polygons of the cluster. + */ + double area_ratio () const; + + /** + * @brief Splits the cluster into multiple other clusters until the desired area ratio is achieved. + * The result is sent to the output iterator. The return value is the number of clusters produced. + * If the area ratio of the cluster is below the limit, no splitting happens and 0 is returned. + */ + template + size_t split (double max_area_ratio, Iter &output) const; + + /** + * @brief Gets a vector of layers inside the cluster + */ + std::vector layers () const; + /** * @brief Gets the total number of shapes in this cluster - */ size_t size () const + */ + size_t size () const { return m_size; } diff --git a/src/db/unit_tests/dbHierNetworkProcessorTests.cc b/src/db/unit_tests/dbHierNetworkProcessorTests.cc index 0fba6ec8e..b58f0460a 100644 --- a/src/db/unit_tests/dbHierNetworkProcessorTests.cc +++ b/src/db/unit_tests/dbHierNetworkProcessorTests.cc @@ -275,6 +275,38 @@ static std::string local_clusters_to_string (const db::local_clusters &cluste return s; } +TEST(12_LocalClusterSplitByAreaRatio) +{ + db::GenericRepository repo; + db::Connectivity conn; + conn.connect (0); + conn.connect (1); + conn.connect (2); + + db::local_cluster cluster (17); + cluster.add (db::PolygonRef (db::Polygon (db::Box (0, 0, 20, 20)), repo), 0); + cluster.add (db::PolygonRef (db::Polygon (db::Box (0, 0, 1000, 20)), repo), 0); + cluster.add (db::PolygonRef (db::Polygon (db::Box (1000, 0, 1020, 1000)), repo), 1); + cluster.add (db::PolygonRef (db::Polygon (db::Box (0, 1000, 1000, 1020)), repo), 2); + + std::list > out; + std::back_insert_iterator > > iout = std::back_inserter (out); + size_t n = cluster.split (10.0, iout); + + EXPECT_EQ (n, size_t (3)); + EXPECT_EQ (out.size (), size_t (3)); + + std::list >::const_iterator i = out.begin (); + EXPECT_EQ (local_cluster_to_string (*i, conn), "[0](0,0;0,20;20,20;20,0);[0](0,0;0,20;1000,20;1000,0)"); + EXPECT_EQ (i->id (), size_t (17)); + ++i; + EXPECT_EQ (local_cluster_to_string (*i, conn), "[1](1000,0;1000,1000;1020,1000;1020,0)"); + EXPECT_EQ (i->id (), size_t (17)); + ++i; + EXPECT_EQ (local_cluster_to_string (*i, conn), "[2](0,1000;0,1020;1000,1020;1000,1000)"); + EXPECT_EQ (i->id (), size_t (17)); +} + TEST(20_LocalClustersBasic) { db::Layout layout;