@@ -517,7 +517,7 @@ def _rule_min_clearance(
517517 pid = prim .id
518518 bbox = prim .bbox
519519 if self .edb .grpc :
520- points = [[pt .x , pt .y ] for pt in prim .polygon_data .without_arcs ().points ]
520+ points = [[pt .x . value , pt .y . value ] for pt in prim .polygon_data .without_arcs ().points ]
521521 else :
522522 points = prim .polygon_data .points_without_arcs
523523
@@ -631,16 +631,23 @@ def _rule_min_annular_ring(self, rule: MinAnnularRing, max_workers: int = None):
631631 via_def = padstacks_definitions [via .padstack_definition ]
632632
633633 # Skip if no hole properties (non-drilled padstack)
634- if not via_def .hole_properties :
635- continue
634+ if self .edb .grpc :
635+ if not via_def .hole_diameter :
636+ continue
637+ else :
638+ if not via_def .hole_properties :
639+ continue
636640
637641 # Some padstacks may not have pad shapes either
638642 if not via_def .pad_by_layer :
639643 continue
640644
641645 first_pad = next (iter (via_def .pad_by_layer .values ()))
642646 od = first_pad .parameters_values [0 ]
643- id_ = via_def .hole_properties [0 ]
647+ if self .edb .grpc :
648+ id_ = via_def .id
649+ else :
650+ id_ = via_def .hole_properties [0 ]
644651 via_data .append ({"via_name" : via .name , "od" : od , "id" : id_ })
645652
646653 # === STEP 2: Multi-threaded computation ===
@@ -664,32 +671,31 @@ def check_via(via_entry):
664671 def _rule_copper_balance (self , rule : CopperBalance ):
665672 max_imbalance = self .edb .value (rule .max_percent )
666673
667- # Snapshot data for thread safety
674+ # Snapshot data
668675 primitives_by_layer = dict (self .edb .modeler .primitives_by_layer )
669676 layout_outline = [prim for prim in self .edb .modeler .primitives if prim .layer .name .lower () == "outline" ]
670677 if not layout_outline :
671678 raise ValueError ("No outline primitive found in the layout." )
672- area_board = layout_outline [0 ].polygon_data .area
679+ if self .edb .grpc :
680+ area_board = layout_outline [0 ].polygon_data .area ()
681+ else :
682+ area_board = layout_outline [0 ].polygon_data .area
673683
674- def check_layer (layer , prim_list ):
675- area_copper = sum (prim .polygon_data .area for prim in prim_list )
684+ for layer , prim_list in primitives_by_layer .items ():
685+ if self .edb .grpc :
686+ area_copper = sum (prim .polygon_data .area () for prim in prim_list )
687+ else :
688+ area_copper = sum (prim .polygon_data .area for prim in prim_list )
676689 imbalance = abs (area_copper - area_board / 2 ) / (area_board / 2 ) * 100
677690 if imbalance > max_imbalance :
678- return {
679- "rule" : "copper_balance" ,
680- "layer" : layer ,
681- "imbalance_pct" : imbalance ,
682- "limit_pct" : max_imbalance ,
683- }
684- return None
685-
686- workers = max (1 , (os .cpu_count () or 2 ) - 1 )
687-
688- with ThreadPoolExecutor (max_workers = workers ) as executor :
689- results = list (executor .map (lambda kv : check_layer (kv [0 ], kv [1 ]), primitives_by_layer .items ()))
690-
691- # Append results in the main thread only (lock-free)
692- self .violations .extend (r for r in results if r is not None )
691+ self .violations .append (
692+ {
693+ "rule" : "copper_balance" ,
694+ "layer" : layer ,
695+ "imbalance_pct" : imbalance ,
696+ "limit_pct" : max_imbalance ,
697+ }
698+ )
693699
694700 # High-speed rules
695701 def _rule_diff_pair_length_match (self , rule : DiffPairLengthMatch ):
@@ -727,33 +733,38 @@ def check_pair(pair):
727733 def _rule_back_drill_stub_length (self , rule : BackDrillStubLength ):
728734 max_stub = self .edb .value (rule .value )
729735
730- # Snapshot data for thread safety
736+ # Snapshot data for safety
731737 padstack_instances = list (self .edb .padstacks .instances .values ())
732738 layers = dict (self .edb .stackup .layers )
733739
734- def check_via (via ):
735- start = via .layer_range_names [0 ]
736- stop = via .layer_range_names [- 1 ]
737- if via .backdrill_parameters :
740+ for via in padstack_instances :
741+ layer_range = via .layer_range_names
742+ if layer_range :
743+ start = layer_range [0 ]
744+ stop = layer_range [- 1 ]
745+
746+ is_backdrilled = False
747+ if self .edb .grpc :
748+ if via .backdrill_diameter :
749+ is_backdrilled = True
750+ else :
751+ if via .backdrill_parameters :
752+ is_backdrilled = True
753+
754+ if is_backdrilled :
738755 via_length = abs (layers [start ].upper_elevation - layers [stop ].lower_elevation )
739756 if via .backdrill_type == "layer_drill" :
740757 if via .backdrill_bottom :
741758 stub = abs (via_length - layers [via .backdrill_parameters [0 ]].lower_elevation )
742759 else :
743760 stub = abs (via_length - layers [via .backdrill_parameters [0 ]].upper_elevation )
744761 else :
745- stub = 0 # If other drill types exist, handle here
746- if stub > max_stub :
747- return {"rule" : "back_drill_stub_length" , "via" : via .name , "stub_um" : stub , "limit_um" : max_stub }
748- return None
762+ stub = 0 # other drill types can be handled here
749763
750- workers = max (1 , (os .cpu_count () or 2 ) - 1 )
751-
752- with ThreadPoolExecutor (max_workers = workers ) as executor :
753- results = list (executor .map (check_via , padstack_instances ))
754-
755- # Merge results lock-free in main thread
756- self .violations .extend (r for r in results if r is not None )
764+ if stub > max_stub :
765+ self .violations .append (
766+ {"rule" : "back_drill_stub_length" , "via" : via .name , "stub_um" : stub , "limit_um" : max_stub }
767+ )
757768
758769 # Export utilities
759770 def to_ipc356a (self , file_path : str ) -> None :
@@ -786,7 +797,10 @@ def to_ipc356a(self, file_path: str) -> None:
786797 f .write (f"NET { net_name } \n " )
787798 for prim in net .primitives :
788799 if hasattr (prim , "polygon_data" ):
789- points = prim .polygon_data .points_without_arcs
800+ if self .edb .grpc :
801+ points = [[pt .x .value , pt .y .value ] for pt in prim .polygon_data .without_arcs ().points ]
802+ else :
803+ points = prim .polygon_data .points_without_arcs
790804 if points :
791805 coords = " " .join (f"x:{ x } , y:{ y } " for x , y in points )
792806 f .write (f" P { coords } \n " )
0 commit comments