diff --git a/src/db/db/dbAsIfFlatRegion.h b/src/db/db/dbAsIfFlatRegion.h index 836c73b4f..cb6bde618 100644 --- a/src/db/db/dbAsIfFlatRegion.h +++ b/src/db/db/dbAsIfFlatRegion.h @@ -138,6 +138,11 @@ public: virtual RegionDelegate *add (const Region &other) const; + virtual RegionDelegate *peel (double /*complexity_factor*/) const + { + return const_cast (this); + } + virtual RegionDelegate *selected_outside (const Region &other) const { return selected_interacting_generic (other, 1, false, Positive, size_t (1), std::numeric_limits::max ()).first; diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index 81988f1fe..716a9e6c4 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -880,6 +880,140 @@ DeepRegion::nets (LayoutToNetlist *l2n, NetPropertyMode prop_mode, const tl::Var return new db::DeepRegion (result); } +namespace { + +/** + * @brief Implements a boolean AND or NOT operation with property handling + */ +class DB_PUBLIC PushHierLocalOperationWithProperties + : public local_operation, db::object_with_properties, db::object_with_properties > +{ +public: + PushHierLocalOperationWithProperties (double complexity_factor) + : local_operation, db::object_with_properties, db::object_with_properties > (), + m_complexity_factor (complexity_factor) + { + // .. nothing yet .. + } + + OnEmptyIntruderHint on_empty_intruder_hint () const { return Copy; } + + std::string description () const + { + return tl::to_string (tr ("'peel' operation")); + } + + + virtual void do_compute_local (db::Layout *layout, db::Cell * /*subject_cell*/, const shape_interactions, db::object_with_properties > &interactions, std::vector > > &results, const db::LocalProcessorBase *proc) const + { + tl_assert (results.size () == 1); + auto &result = results.front (); + + db::EdgeProcessor ep; + + for (auto i = interactions.begin (); i != interactions.end (); ++i) { + + const auto &subject = interactions.subject_shape (i->first); + db::properties_id_type prop_id = subject.properties_id (); + + if (i->second.empty ()) { + + result.insert (subject); + + } else { + + ep.clear (); + + const auto &subject = interactions.subject_shape (i->first); + for (auto e = subject.begin_edge (); ! e.at_end(); ++e) { + ep.insert (*e, 0); + } + + size_t p2 = 1; + for (auto ii = i->second.begin (); ii != i->second.end (); ++ii) { + const auto &intruder = interactions.intruder_shape (*ii); + for (auto e = intruder.second.begin_edge (); ! e.at_end(); ++e) { + ep.insert (*e, p2); + } + p2 += 2; + } + + std::unordered_set > result1; + + db::BooleanOp op (db::BooleanOp::ANotB); + db::polygon_ref_generator_with_properties > pr (layout, result1, prop_id); + db::PolygonSplitter splitter (pr, proc->area_ratio (), proc->max_vertex_count ()); + db::PolygonGenerator pg (splitter, true, true); + ep.set_base_verbosity (50); + ep.process (pg, op); + + if (result1.empty ()) { + + // shortcut: nothing to do + + } else if (m_complexity_factor < 0.0) { + + // no complexity limit + result.insert (result1.begin (), result1.end ()); + + } else if (m_complexity_factor == 0.0) { + + // only remove shape if it is really entirely covered in this case + result.insert (subject); + + } else { + + size_t vertices_before = subject.vertices (); + size_t vertices_after = 0; + for (auto r = result1.begin (); r != result1.end (); ++r) { + vertices_after += r->vertices (); + } + + if (floor (0.5 + m_complexity_factor * vertices_before) >= vertices_after) { + result.insert (result1.begin (), result1.end ()); + } else { + result.insert (subject); + } + + } + + } + + } + } + +private: + double m_complexity_factor; +}; + +} + +RegionDelegate * +DeepRegion::peel (double complexity_factor) const +{ + if (empty ()) { + // we can return "this", as this method is only intended for in-place execution inside Region + return const_cast (this); + } + + DeepLayer dl_out (deep_layer ().derived ()); + + PushHierLocalOperationWithProperties op (complexity_factor); + + db::local_processor proc (const_cast (&deep_layer ().layout ()), const_cast (&deep_layer ().initial_cell ()), deep_layer ().breakout_cells ()); + configure_proc (proc); + proc.set_threads (deep_layer ().store ()->threads ()); + proc.set_area_ratio (deep_layer ().store ()->max_area_ratio ()); + proc.set_max_vertex_count (deep_layer ().store ()->max_vertex_count ()); + + // with this setting, only top-down interactions are considered + proc.set_top_down (true); + + proc.run (&op, deep_layer ().layer (), deep_layer ().layer (), dl_out.layer ()); + + return new DeepRegion (dl_out); +} + RegionDelegate * DeepRegion::and_with (const Region &other, PropertyConstraint property_constraint) const { diff --git a/src/db/db/dbDeepRegion.h b/src/db/db/dbDeepRegion.h index ad26af8dc..10a9277f1 100644 --- a/src/db/db/dbDeepRegion.h +++ b/src/db/db/dbDeepRegion.h @@ -140,6 +140,8 @@ public: virtual RegionDelegate *sized_inside (const Region &inside, bool outside, coord_type d, int steps, unsigned int mode) const; virtual RegionDelegate *sized_inside (const Region &inside, bool outside, coord_type dx, coord_type dy, int steps, unsigned int mode) const; + virtual RegionDelegate *peel (double complexity_factor) const; + virtual void insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const; virtual RegionDelegate *nets (LayoutToNetlist *l2n, NetPropertyMode prop_mode, const tl::Variant &net_prop_name, const std::vector *nets) const; diff --git a/src/db/db/dbEmptyRegion.h b/src/db/db/dbEmptyRegion.h index fb551c9b5..8a1caa9ec 100644 --- a/src/db/db/dbEmptyRegion.h +++ b/src/db/db/dbEmptyRegion.h @@ -111,6 +111,8 @@ public: virtual RegionDelegate *add_in_place (const Region &other); virtual RegionDelegate *add (const Region &other) const; + virtual RegionDelegate *peel (double /*complexity_factor*/) const { return new EmptyRegion (); } + virtual RegionDelegate *selected_outside (const Region &) const { return new EmptyRegion (); } virtual RegionDelegate *selected_not_outside (const Region &) const { return new EmptyRegion (); } virtual std::pair selected_outside_pair (const Region &) const { return std::make_pair (new EmptyRegion (), new EmptyRegion ()); } diff --git a/src/db/db/dbHierProcessor.cc b/src/db/db/dbHierProcessor.cc index a6af1f211..26cc7608e 100644 --- a/src/db/db/dbHierProcessor.cc +++ b/src/db/db/dbHierProcessor.cc @@ -744,7 +744,7 @@ local_processor_result_computation_task::perform () // LocalProcessorBase implementation LocalProcessorBase::LocalProcessorBase () - : m_report_progress (true), m_nthreads (0), m_max_vertex_count (0), m_area_ratio (0.0), m_boolean_core (false), + : m_report_progress (true), m_nthreads (0), m_max_vertex_count (0), m_area_ratio (0.0), m_top_down (false), m_boolean_core (false), m_base_verbosity (30), mp_vars (0), mp_current_cell (0) { // .. nothing yet .. @@ -1034,84 +1034,90 @@ void local_processor::compute_contexts (local_processor_contexts::const_iterator il = contexts.intruder_layers ().begin (); il != contexts.intruder_layers ().end (); ++il) { + // in top-down mode we are not interested in cell-to-cell interactions, nor shape-to-instance interactions + // except local ones (shape-to-child cells), hence we skip this part + if (! top_down ()) { - db::box_convert inst_bci (*mp_intruder_layout, contexts.actual_intruder_layer (*il)); + // TODO: can we shortcut this if interactions is empty? + for (std::vector::const_iterator il = contexts.intruder_layers ().begin (); il != contexts.intruder_layers ().end (); ++il) { - db::box_scanner2 scanner; - interaction_registration_inst2inst rec (mp_subject_layout, contexts.subject_layer (), mp_intruder_layout, contexts.actual_intruder_layer (*il), contexts.is_foreign (*il), dist, &interactions); + db::box_convert inst_bci (*mp_intruder_layout, contexts.actual_intruder_layer (*il)); - unsigned int id = 0; + db::box_scanner2 scanner; + interaction_registration_inst2inst rec (mp_subject_layout, contexts.subject_layer (), mp_intruder_layout, contexts.actual_intruder_layer (*il), contexts.is_foreign (*il), dist, &interactions); - if (subject_cell == intruder_cell) { + unsigned int id = 0; - // Use the same id's for same instances - this way we can easily detect same instances - // and don't make them self-interacting + if (subject_cell == intruder_cell) { - for (db::Cell::const_iterator i = subject_cell->begin (); !i.at_end (); ++i) { - unsigned int iid = ++id; - if (! inst_bcs (i->cell_inst ()).empty () && ! subject_cell_is_breakout (i->cell_index ())) { - scanner.insert1 (&i->cell_inst (), iid); - } - if (! inst_bci (i->cell_inst ()).empty () && ! intruder_cell_is_breakout (i->cell_index ())) { - scanner.insert2 (&i->cell_inst (), iid); - } - } + // Use the same id's for same instances - this way we can easily detect same instances + // and don't make them self-interacting - } else { - - for (db::Cell::const_iterator i = subject_cell->begin (); !i.at_end (); ++i) { - if (! inst_bcs (i->cell_inst ()).empty () && ! subject_cell_is_breakout (i->cell_index ())) { - scanner.insert1 (&i->cell_inst (), ++id); - } - } - - if (intruder_cell) { - for (db::Cell::const_iterator i = intruder_cell->begin (); !i.at_end (); ++i) { + for (db::Cell::const_iterator i = subject_cell->begin (); !i.at_end (); ++i) { + unsigned int iid = ++id; + if (! inst_bcs (i->cell_inst ()).empty () && ! subject_cell_is_breakout (i->cell_index ())) { + scanner.insert1 (&i->cell_inst (), iid); + } if (! inst_bci (i->cell_inst ()).empty () && ! intruder_cell_is_breakout (i->cell_index ())) { - scanner.insert2 (&i->cell_inst (), ++id); + scanner.insert2 (&i->cell_inst (), iid); } } + + } else { + + for (db::Cell::const_iterator i = subject_cell->begin (); !i.at_end (); ++i) { + if (! inst_bcs (i->cell_inst ()).empty () && ! subject_cell_is_breakout (i->cell_index ())) { + scanner.insert1 (&i->cell_inst (), ++id); + } + } + + if (intruder_cell) { + for (db::Cell::const_iterator i = intruder_cell->begin (); !i.at_end (); ++i) { + if (! inst_bci (i->cell_inst ()).empty () && ! intruder_cell_is_breakout (i->cell_index ())) { + scanner.insert2 (&i->cell_inst (), ++id); + } + } + } + } + for (std::set::const_iterator i = intruders.first.begin (); i != intruders.first.end (); ++i) { + if (! inst_bci (*i).empty ()) { + scanner.insert2 (i.operator-> (), ++id); + } + } + + scanner.process (rec, dist, inst_bcs, inst_bci); + } - for (std::set::const_iterator i = intruders.first.begin (); i != intruders.first.end (); ++i) { - if (! inst_bci (*i).empty ()) { - scanner.insert2 (i.operator-> (), ++id); + if (! intruders.second.empty () || ! intruder_shapes.empty ()) { + + db::box_scanner2 scanner; + db::addressable_object_from_shape heap; + interaction_registration_inst2shape rec (mp_subject_layout, contexts.subject_layer (), dist, &interactions); + + for (db::Cell::const_iterator i = subject_cell->begin (); !i.at_end (); ++i) { + if (! inst_bcs (i->cell_inst ()).empty () && ! subject_cell_is_breakout (i->cell_index ())) { + scanner.insert1 (&i->cell_inst (), 0); + } } - } - scanner.process (rec, dist, inst_bcs, inst_bci); - - } - - if (! intruders.second.empty () || ! intruder_shapes.empty ()) { - - db::box_scanner2 scanner; - db::addressable_object_from_shape heap; - interaction_registration_inst2shape rec (mp_subject_layout, contexts.subject_layer (), dist, &interactions); - - for (db::Cell::const_iterator i = subject_cell->begin (); !i.at_end (); ++i) { - if (! inst_bcs (i->cell_inst ()).empty () && ! subject_cell_is_breakout (i->cell_index ())) { - scanner.insert1 (&i->cell_inst (), 0); + for (typename std::map >::const_iterator il = intruders.second.begin (); il != intruders.second.end (); ++il) { + for (typename std::set::const_iterator i = il->second.begin (); i != il->second.end (); ++i) { + scanner.insert2 (i.operator-> (), il->first); + } } - } - for (typename std::map >::const_iterator il = intruders.second.begin (); il != intruders.second.end (); ++il) { - for (typename std::set::const_iterator i = il->second.begin (); i != il->second.end (); ++i) { - scanner.insert2 (i.operator-> (), il->first); + for (std::map::const_iterator im = intruder_shapes.begin (); im != intruder_shapes.end (); ++im) { + for (db::Shapes::shape_iterator i = im->second->begin (shape_flags ()); !i.at_end (); ++i) { + scanner.insert2 (heap (*i), im->first); + } } - } - for (std::map::const_iterator im = intruder_shapes.begin (); im != intruder_shapes.end (); ++im) { - for (db::Shapes::shape_iterator i = im->second->begin (shape_flags ()); !i.at_end (); ++i) { - scanner.insert2 (heap (*i), im->first); - } - } + scanner.process (rec, dist, inst_bcs, db::box_convert ()); - scanner.process (rec, dist, inst_bcs, db::box_convert ()); + } } @@ -1417,14 +1423,14 @@ local_processor::compute_local_cell (const db::local_processor_conte } } - // local shapes vs. child cell - db::box_convert inst_bci (*mp_intruder_layout, ail); typename std::map >::const_iterator ipl = intruders.second.find (*il); static std::set empty_intruders; - if (! subject_shapes->empty () && (intruder_shapes || ipl != intruders.second.end ())) { + // local shapes vs. local shapes + + if (! top_down () && ! subject_shapes->empty () && (intruder_shapes || ipl != intruders.second.end ())) { if (subject_cell == intruder_cell && contexts.subject_layer () == ail && !foreign) { @@ -1439,6 +1445,8 @@ local_processor::compute_local_cell (const db::local_processor_conte } + // local shapes vs. child cells + if (! subject_shapes->empty () && ! ((! intruder_cell || intruder_cell->begin ().at_end ()) && intruders.first.empty ())) { db::box_scanner2 scanner; @@ -1452,7 +1460,7 @@ local_processor::compute_local_cell (const db::local_processor_conte unsigned int inst_id = 0; - if (subject_cell == intruder_cell && contexts.subject_layer () == ail && !foreign) { + if (! top_down () && subject_cell == intruder_cell && contexts.subject_layer () == ail && !foreign) { // Same cell, same layer -> no shape to child instance interactions because this will be taken care of // by the instances themselves (and their intruders). This also means, we prefer to deal with diff --git a/src/db/db/dbHierProcessor.h b/src/db/db/dbHierProcessor.h index c52dd372e..8d446641a 100644 --- a/src/db/db/dbHierProcessor.h +++ b/src/db/db/dbHierProcessor.h @@ -465,6 +465,16 @@ public: return m_nthreads; } + void set_top_down (bool f) + { + m_top_down = f; + } + + bool top_down () const + { + return m_top_down; + } + void set_max_vertex_count (size_t max_vertex_count) { m_max_vertex_count = max_vertex_count; @@ -525,6 +535,7 @@ private: unsigned int m_nthreads; size_t m_max_vertex_count; double m_area_ratio; + bool m_top_down; bool m_boolean_core; int m_base_verbosity; const db::VariantsCollectorBase *mp_vars; diff --git a/src/db/db/dbRegion.h b/src/db/db/dbRegion.h index ce3aeb470..655462a01 100644 --- a/src/db/db/dbRegion.h +++ b/src/db/db/dbRegion.h @@ -1331,6 +1331,25 @@ public: return std::make_pair (Region (res.first), Region (res.second)); } + /** + * @brief For deep regions, remove parts of shapes which are covered by child cell shapes (push shapes into hierarchy) + * + * This will reduce the hierarchical load. This means that shapes that do not add information + * will be removed, so their interactions with child cells does not need to be considered. + * These shapes are - maybe partially - "peeled" from upper hierarchy layers. + * + * The complexity factor indicates by how much the complexity of the resulting polygons + * (counted in terms of vertexes) can increase before a shape is left as it was. + * A negative complexity factor indicates, that no such limit exists. A zero complexity factor + * means that only shapes are removed if they are covered entirely by shapes from below the + * hierarchy. + */ + Region &peel (double complexity_factor = 0.0) + { + set_delegate (mp_delegate->peel (complexity_factor)); + return *this; + } + /** * @brief Selects all polygons of this region which are completely outside polygons from the other region * diff --git a/src/db/db/dbRegionDelegate.h b/src/db/db/dbRegionDelegate.h index ff1c90fe4..6c266350a 100644 --- a/src/db/db/dbRegionDelegate.h +++ b/src/db/db/dbRegionDelegate.h @@ -248,6 +248,8 @@ public: virtual RegionDelegate *add (const Region &other) const = 0; virtual std::pair andnot_with (const Region &other, PropertyConstraint prop_constraint) const = 0; + virtual RegionDelegate *peel (double complexity_factor) const = 0; + virtual RegionDelegate *selected_outside (const Region &other) const = 0; virtual RegionDelegate *selected_not_outside (const Region &other) const = 0; virtual std::pair selected_outside_pair (const Region &other) const = 0; diff --git a/src/db/db/gsiDeclDbRegion.cc b/src/db/db/gsiDeclDbRegion.cc index 2f2dd85fa..528342e4a 100644 --- a/src/db/db/gsiDeclDbRegion.cc +++ b/src/db/db/gsiDeclDbRegion.cc @@ -2732,6 +2732,20 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\n" "This method has been introduced in version 0.29.3." ) + + method ("peel", &db::Region::peel, gsi::arg ("complexity_factor", -1.0, "unlimited"), + "@brief Removes shapes parts which are overlapping with child cell shapes, reducing hierarchical load.\n" + "\n" + "This method will reduce the hierarchical load. This means that shapes that do not add information\n" + "will be removed, so their interactions with child cells does not need to be considered.\n" + "These shapes are - maybe partially - \"peeled\" from upper hierarchy layers.\n" + "\n" + "The complexity factor determines if the subtraction is rejected when the complexity - measured as polygon " + "vertex count - increases by more than the given factor. This allows trading off hierarchical complexity vs. " + "polygon complexity. A negative factor means no rejection. A factor of zero means that only shapes are removed " + "which are entirely covered by shapes from below the hierarchy.\n" + "\n" + "This method has been introduced in version 0.30.8." + ) + method_ext ("andnot", &andnot, gsi::arg ("other"), gsi::arg ("property_constraint", db::IgnoreProperties, "IgnoreProperties"), "@brief Returns the boolean AND and NOT between self and the other region\n" "\n" diff --git a/src/db/unit_tests/dbDeepRegionTests.cc b/src/db/unit_tests/dbDeepRegionTests.cc index 55fc38675..108b5ed4b 100644 --- a/src/db/unit_tests/dbDeepRegionTests.cc +++ b/src/db/unit_tests/dbDeepRegionTests.cc @@ -3290,3 +3290,54 @@ TEST(processed_delivers_polygon_refs) EXPECT_EQ (rx.to_string (), "(0,0;0,2000;2000,2000;2000,0);(0,0;0,2000;2000,2000;2000,0/200,200;1800,200;1800,1800;200,1800){n=>42}"); } + +TEST(deep_region_peel) +{ + db::Layout ly; + { + std::string fn (tl::testdata ()); + fn += "/algo/deep_region_peel.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + db::DeepShapeStore dss; + + unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0)); + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + + db::RecursiveShapeIterator si1 (ly, top_cell, l1); + si1.apply_property_translator (db::PropertiesTranslator::make_pass_all ()); + + db::RecursiveShapeIterator si2 (ly, top_cell, l2); + si2.apply_property_translator (db::PropertiesTranslator::make_pass_all ()); + + db::Region r1 (si1, dss); + db::Region r2 (si2, dss); + + unsigned int l1001 = ly.get_layer (db::LayerProperties (1001, 0)); + unsigned int l1002 = ly.get_layer (db::LayerProperties (1002, 0)); + unsigned int l1011 = ly.get_layer (db::LayerProperties (1011, 0)); + unsigned int l1012 = ly.get_layer (db::LayerProperties (1012, 0)); + unsigned int l1021 = ly.get_layer (db::LayerProperties (1021, 0)); + unsigned int l1022 = ly.get_layer (db::LayerProperties (1022, 0)); + unsigned int l1031 = ly.get_layer (db::LayerProperties (1031, 0)); + unsigned int l1032 = ly.get_layer (db::LayerProperties (1032, 0)); + + db::Region (r1).peel (-1.0).insert_into (&ly, top_cell_index, l1001); + db::Region (r2).peel (-1.0).insert_into (&ly, top_cell_index, l1002); + db::Region (r1).peel (0.0).insert_into (&ly, top_cell_index, l1011); + db::Region (r2).peel (0.0).insert_into (&ly, top_cell_index, l1012); + db::Region (r1).peel (2.0).insert_into (&ly, top_cell_index, l1021); + db::Region (r2).peel (2.0).insert_into (&ly, top_cell_index, l1022); + db::Region (r1).peel (4.0).insert_into (&ly, top_cell_index, l1031); + db::Region (r2).peel (4.0).insert_into (&ly, top_cell_index, l1032); + + CHECKPOINT(); + db::compare_layouts (_this, ly, tl::testdata () + "/algo/deep_region_peel_au.gds"); +} + diff --git a/testdata/algo/deep_region_peel.gds b/testdata/algo/deep_region_peel.gds new file mode 100644 index 000000000..0dba10212 Binary files /dev/null and b/testdata/algo/deep_region_peel.gds differ diff --git a/testdata/algo/deep_region_peel_au.gds b/testdata/algo/deep_region_peel_au.gds new file mode 100644 index 000000000..174ecb973 Binary files /dev/null and b/testdata/algo/deep_region_peel_au.gds differ