Source code for qiskit_metal.toolbox_metal.bounds_for_path_and_poly_tables

# -*- coding: utf-8 -*-
from re import sub
from typing import List, Tuple, Union
from copy import deepcopy

import pandas as pd


[docs] def determine_larger_box( minx: Union[None, float], miny: Union[None, float], maxx: Union[None, float], maxy: Union[None, float], chip_box: tuple, ) -> Tuple[float, float, float, float]: """Return box which includes the two boxes. Args: minx (Union[None, float]): Minimum of x coordinate miny (Union[None, float]): Minimum of y coordinate maxx (Union[None, float]): Maximum of x coordinate maxy (Union[None, float]): Maximum of y coordinate chip_box (tuple): Second box in following format: minx, miny, maxx, maxy Returns: Tuple[float, float, float, float]: The size: minx, miny, maxx, maxy of box which includes both boxes. """ large_minx, large_miny, large_maxx, large_maxy = None, None, None, None if minx and miny and maxx and maxy: chip_minx, chip_miny, chip_maxx, chip_maxy = chip_box large_minx = min(minx, chip_minx) large_miny = min(miny, chip_miny) large_maxx = max(maxx, chip_maxx) large_maxy = max(maxy, chip_maxy) else: # First time getting chip size, so just use chip_box large_minx, large_miny, large_maxx, large_maxy = chip_box return large_minx, large_miny, large_maxx, large_maxy
[docs] class BoundsForPathAndPolyTables: """Create class which can be used by multiple renderers. In particular, this class assumes a LayerStack is being used within QDesign. """ def __init__(self, design: "MultiPlanar"): self.design = design self.chip_names_matched = None # bool self.valid_chip_names = None # set of valid chip names from layer_stack
[docs] def get_bounds_of_path_and_poly_tables( self, box_plus_buffer: bool, qcomp_ids: List, case: int, x_buff: float, y_buff: float, ) -> tuple[ tuple[float, float, float, float], pd.DataFrame, pd.DataFrame, bool, Union[None, set], ]: """Return bounds and concatenated geometry tables for selected components. Args: box_plus_buffer: If True, use selected components' bounding box plus the supplied buffer; otherwise use the chip size. qcomp_ids: Component ids to include (may be empty to include all). case: Return code from ``QRenderer.get_unique_component_ids``. x_buff: Buffer to add in x when ``box_plus_buffer`` is True. y_buff: Buffer to add in y when ``box_plus_buffer`` is True. Returns: tuple: Bounding box (minx, miny, maxx, maxy). pd.DataFrame: Path and poly tables concatenated for the selection. pd.DataFrame: Path, poly, and junction tables concatenated. bool: True if chip names match between design and layer stack. set | None: Valid chip names if they match, otherwise None. """ box_for_xy_bounds = None self.chip_names_matched = None self.valid_chip_names = None path_dataframe = self.design.qgeometry.tables["path"] poly_dataframe = self.design.qgeometry.tables["poly"] junction_dataframe = self.design.qgeometry.tables["junction"] # NOTE:get_box_for_xy_bounds populates self.chip_names_matched and self.valid_chip_names chip_minx, chip_miny, chip_maxx, chip_maxy = self.get_box_for_xy_bounds() chip_bounds_xy = (chip_minx, chip_miny, chip_maxx, chip_maxy) if box_plus_buffer: # Based on component selection, determine the bounds for box_plus_buffer. frames = None frames_with_jj = None path_and_poly_with_valid_comps = None if case == 2: # One or more components not in QDesign. self.design.logger.warning("One or more components not found.") elif case == 1: # Render all components frames = [path_dataframe, poly_dataframe] frames_with_jj = [path_dataframe, poly_dataframe, junction_dataframe] else: # Strict subset rendered. mask_path = path_dataframe["component"].isin(qcomp_ids) mask_poly = poly_dataframe["component"].isin(qcomp_ids) mask_junction = junction_dataframe["component"].isin(qcomp_ids) subset_path_df = path_dataframe[mask_path] subset_poly_df = poly_dataframe[mask_poly] subset_junction_df = junction_dataframe[mask_junction] frames = [subset_path_df, subset_poly_df] frames_with_jj = [subset_path_df, subset_poly_df, subset_junction_df] # Concat the frames and then determine the total bounds of all the geometries. # maybe, change name to package_cavity path_and_poly_with_valid_comps = pd.concat(frames, ignore_index=True) path_poly_and_junction_valid_comps = pd.concat( frames_with_jj, ignore_index=True ) minx, miny, maxx, maxy = list( path_and_poly_with_valid_comps["geometry"].total_bounds ) # minx, miny, maxx, maxy = list( # pd.concat(frames, ignore_index=True)['geometry'].total_bounds) # # Add the buffer, using options for renderer. # x_buff = parse_entry(self._options["x_buffer_width_mm"]) # y_buff = parse_entry(self._options["y_buffer_width_mm"]) minx -= x_buff miny -= y_buff maxx += x_buff maxy += y_buff box_for_xy_bounds = (minx, miny, maxx, maxy) safe_xy_bounds = self.ensure_component_box_smaller_than_chip_box_( box_for_xy_bounds, chip_bounds_xy ) return ( safe_xy_bounds, path_and_poly_with_valid_comps, path_poly_and_junction_valid_comps, self.chip_names_matched, self.valid_chip_names, ) else: # Incorporate all the chip sizes. frames = [path_dataframe, poly_dataframe] frames_with_jj = [path_dataframe, poly_dataframe, junction_dataframe] path_and_poly_with_valid_comps = pd.concat(frames, ignore_index=True) path_poly_and_junction_valid_comps = pd.concat( frames_with_jj, ignore_index=True ) return ( chip_bounds_xy, path_and_poly_with_valid_comps, path_poly_and_junction_valid_comps, self.chip_names_matched, self.valid_chip_names, )
[docs] @classmethod def ensure_component_box_smaller_than_chip_box_( cls, box_for_xy_bounds: Tuple, chip_bounds_xy: Tuple ) -> Tuple: """If the box_plus_buffer is larger than the aggregate chip bounds from DesignPlanar, use the chip bounds as the cutoff. Args: box_for_xy_bounds (Tuple): In xy plane, the bounding box for the components to render. Box from QGeometry tables. chip_bounds_xy (Tuple): In xy plane, the bounding box for aggregate chip size. Box from MultiPlanar chip size. Returns: Tuple: In xy plane, the box_for_xy_bounds will be limited by the chip_bounds_xy if box_for_xy_bounds is larger than chip_bounds_xy. If chip_bounds_xy is None (bad input), no checking will happen and box_for_xy_bounds will be returned. """ chip_minx, chip_miny, chip_maxx, chip_maxy = chip_bounds_xy if ( chip_minx is None or chip_miny is None or chip_maxx is None or chip_maxy is None ): input_xy_box = deepcopy(box_for_xy_bounds) return input_xy_box box_minx, box_miny, box_maxx, box_maxy = box_for_xy_bounds safe_xy_box = list() # Keep the order of appends in this way. It should match (minx, miny, maxx, maxy) # yapf: disable safe_xy_box.append(chip_minx) if box_minx < chip_minx else safe_xy_box.append(box_minx) safe_xy_box.append(chip_miny) if box_miny < chip_miny else safe_xy_box.append(box_miny) safe_xy_box.append(chip_maxx) if box_maxx > chip_maxx else safe_xy_box.append(box_maxx) safe_xy_box.append(chip_maxy) if box_maxy > chip_maxy else safe_xy_box.append(box_maxy) # yapf: enable return tuple(safe_xy_box)
[docs] def get_box_for_xy_bounds( self, ) -> Union[None, Union[Tuple[float, float, float, float], None]]: """Assuming the chip size is used from Multiplanar design, and list of chip_names comes from layer_stack that will be used to determine the box size for simulation. Returns: Union[None, Union[Tuple[float, float, float, float], None]]: None if not able to get the chip information Tuple holds the box to use for simulation [minx, miny, maxx, maxy] """ self.chip_names_matched, self.valid_chip_names = ( self.are_all_chipnames_in_design() ) minx, miny, maxx, maxy = None, None, None, None if self.chip_names_matched: # Using the chip size from Multiplanar design and z_coord from layer_stack, get the box # for chip_name in self.chip_names_matched: for chip_name in self.valid_chip_names: if self.design.chips[chip_name]["size"]: chip_box, return_code = self.get_x_y_for_chip(chip_name) if return_code == 0: # All was found and good. minx, miny, maxx, maxy = determine_larger_box( minx, miny, maxx, maxy, chip_box ) else: self.chip_size_not_in_chipname_within_design(chip_name) return minx, miny, maxx, maxy
[docs] def are_all_chipnames_in_design(self) -> Tuple[bool, Union[set, None]]: """Using chip names in layer_stack information, then check if the information is in MultiPlanar design. Returns: Tuple[bool, Union[set, None]]: bool if there is a key in design with same chip_name as in layer_stack Union has either None if the names don't match, or the set of chip_names that can be used. """ chip_set_from_design = set(self.design.chips.keys()) chip_set_from_layer_stack = self.design.ls.get_unique_chip_names() if not chip_set_from_layer_stack.issubset(chip_set_from_design): self.chip_names_not_in_design( chip_set_from_layer_stack, chip_set_from_design ) return False, None return True, chip_set_from_layer_stack
[docs] def get_x_y_for_chip(self, chip_name: str) -> Tuple[tuple, int]: """If the chip_name is in self.chips, along with entry for size information then return a tuple=(minx, miny, maxx, maxy). Used for subtraction while exporting design. Args: chip_name (str): Name of chip that you want the size of. Returns: Tuple[tuple, int]: tuple: The exact placement on rectangle coordinate (minx, miny, maxx, maxy). int: 0=all is good 1=chip_name not in self._chips 2=size information missing or no good """ x_y_location = tuple() if chip_name in self.design.chips: if "size" in self.design.chips[chip_name]: size = self.design.parse_value(self.design.chips[chip_name]["size"]) if ( "center_x" in size and "center_y" in size and "size_x" in size and "size_y" in size ): if ( type(size.center_x) in [int, float] and type(size.center_y) in [int, float] and type(size.size_x) in [int, float] and type(size.size_y) in [int, float] ): x_y_location = ( size["center_x"] - (size["size_x"] / 2.0), size["center_y"] - (size["size_y"] / 2.0), size["center_x"] + (size["size_x"] / 2.0), size["center_y"] + (size["size_y"] / 2.0), ) return x_y_location, 0 self.design.logger.warning( f'Size information within self.design.chips[{chip_name}]["size"]' f" is NOT an int or float." ) return x_y_location, 2 self.design.logger.warning( "center_x or center_y or size_x or size_y " f' NOT in self.design.chips[{chip_name}]["size"]' ) return x_y_location, 2 self.design.logger.warning( f"Information for size in NOT in self.design.chips[{chip_name}]" ' dict. Return "None" in tuple.' ) return x_y_location, 2 self.design.logger.warning( f'Chip name "{chip_name}" is not in self.design.chips dict. Return "None" in tuple.' ) return x_y_location, 1
######### Warnings and Errors##################################################
[docs] def chip_names_not_in_design(self, layer_stack_names: set, design_names: set): """ Tell user to check the chip name and data in design. Args: layer_stack_names (set): Chip names from layer_stack. design_names (set): Chip names from design. """ self.design.logger.warning( f"\nThe chip_names from layer_stack are not in design. " f"\n The names in layer_stack:{layer_stack_names}." f"\n The names in design:{design_names}." )
[docs] def chip_size_not_in_chipname_within_design(self, chip_name: str): """ Tell user to check the chip size data within design. Args: chip_name (str): Chip names from layer_stack. """ self.design.logger.error( f"\nThe chip_name:{chip_name} within design. " f"\n Update your QDesign or subclass to see confirm the size information is provided." )