Source code for qiskit_metal.toolbox_metal.layer_stack_handler

# -*- coding: utf-8 -*-

from re import search
import pandas as pd
from typing import List, Tuple, Dict
from typing import Union
from addict import Dict
import os
import logging

from .parsing import TRUE_STR, FALSE_STR


[docs] class LayerStackHandler(): """Use DataFrame to hold information for multiple chips. For all the chips, do NOT reuse the layer-number. However, within the dataframe, we can repeat layer numbers since a layer can have multiple datatypes. """ Col_Names = [ 'chip_name', 'layer', 'datatype', 'material', 'thickness', 'z_coord', 'fill' ] def __init__(self, multi_planar_design: 'MultiPlanar', fname: Union['str', None] = None) -> None: """Use the information in MultiPlanar design to parse_value and get the name of filename for layer stack. Args: multi_planar_design (MultiPlanar): Class that is child of QDesign. """ self.multi_planar_design = multi_planar_design self.logger = self.multi_planar_design.logger # type: logging.Logger self.filename_csv_df = None if hasattr(self.multi_planar_design, 'ls_filename') and self.multi_planar_design.ls_filename: #populate pandas table from data in this file. self.filename_csv_df = self.multi_planar_design.ls_filename elif fname: self.filename_csv_df = fname self.ls_df = None # self.column_names = [ # 'chip_name', 'layer', 'datatype', 'material', 'thickness', # 'z_coord', 'fill' # ] self.layer_stack_default = Dict(chip_name=['main', 'main'], layer=[1, 3], datatype=[0, 0], material=['pec', 'silicon'], thickness=['2um', '-750um'], z_coord=['0um', '0um'], fill=['true', 'true']) self._init_dataframe() self.is_layer_data_unique() def _init_dataframe(self) -> None: """Must check if filename for layerstack is valid before trying to import to a pandas table. """ if self.filename_csv_df: #populate pandas table from data in this file. abs_path = os.path.abspath(self.filename_csv_df) if os.path.isfile(abs_path): self._read_csv_df(abs_path) else: self.logger.error( f'Not able to read file.' f'File:{abs_path} not read. Check the name and path.') else: #enter very basic default data for pandas table. self.ls_df = pd.DataFrame(data=self.layer_stack_default)
[docs] def get_layer_datatype_when_fill_is_true(self) -> Union[dict, None]: """If a row has fill=True, then for each layer/datatype pair, return the rest of information in the row within a dict. Returns: Union[dict, None]: Dict with key being tuple (layer, datatype) Value is dict with key of column name of layerstack, and parsed value of column. None if fill != True """ if self.ls_df is None: abs_path = os.path.abspath(self.filename_csv_df) self.logger.error( f'Not able to read file.' f'File:{abs_path} not read. Check the name and path.') mask = self.ls_df['fill'].astype(str).str.replace( "[']", "", regex=True).isin(TRUE_STR) search_result_df = self.ls_df[mask] if len(search_result_df) > 0: try: layer_datatype_fill = dict() for row in search_result_df.itertuples(): a_key = (row.layer, row.datatype) layer_datatype_fill[a_key] = dict() layer_datatype_fill[a_key]['layer'] = row.layer layer_datatype_fill[a_key]['datatype'] = row.datatype layer_datatype_fill[a_key][ 'thickness'] = self.multi_planar_design.parse_value( row.thickness.strip('\'')) layer_datatype_fill[a_key][ 'z_coord'] = self.multi_planar_design.parse_value( row.z_coord.strip('\'')) layer_datatype_fill[a_key]['material'] = row.material.strip( '\'') layer_datatype_fill[a_key][ 'chip_name'] = row.chip_name.strip('\'') return layer_datatype_fill except Exception as ex: self.logger.error( f'User is not using LayerStackHandler.get_layer_datatype_when_fill_is_true correctly. Check your input file.' f'\nERROR:{ex}') return None
[docs] def get_properties_for_layer_datatype( self, properties: List[str], layer_number: int, datatype: int = 0) -> Union[Tuple[Union[float, str, bool]], None]: """When user provides a layer and datatype, they can get properties from the layer_stack file. The allowed options for properties must be in Col_Names. If any of the properties are not in Col_Names, None will be returned. Otherwise a Tuple will be returned with properties in the same order as provided in input variable properties. Args: properties (List[str]): The column(s) within the layer stack that you want for a row based on layer, and datatype. layer_number (int): The layer number within the column denoted by layer. datatype (int, optional): The datatype within the column denoted by datatype. Defaults to 0. Returns: Union[Tuple[Union[float, str, bool]], None]: If the search data provided in the arguments are not in the layer_stack file, None will be returned. If the search values are found in layer_stack file, then a Tuple will be returned with the requested properties in the same order as provided in input variable denoted by properties. """ if not properties: return None # Check if parameter is not a subset if Col_Names T. if not set(properties).issubset(set(self.Col_Names)): self._warning_properties(properties) return None props = Dict() thickness = 0.0 z_coord = 0.0 material = None fill_value = None chip_name = None if self.ls_df is None: abs_path = os.path.abspath(self.filename_csv_df) self.logger.error( f'Not able to read file.' f'File:{abs_path} not read. Check the name and path.') mask = (self.ls_df['layer'] == layer_number) & (self.ls_df['datatype'] == datatype) search_result_df = self.ls_df[mask] if len(search_result_df) > 0: try: thickness = self.multi_planar_design.parse_value( search_result_df.thickness.iloc[0].strip('\'')) z_coord = self.multi_planar_design.parse_value( search_result_df.z_coord.iloc[0].strip('\'')) material = search_result_df.material.iloc[0].strip('\'') chip_name = search_result_df.chip_name.iloc[0].strip('\'') value = search_result_df.fill.iloc[0].strip('\'') if value in TRUE_STR: fill_value = True elif value in FALSE_STR: fill_value = False else: self.logger.warning( f'The \"fill\" value is neither True nor False.' f'You have:{value}. ' f'Will return NULL for fill value.') props['thickness'] = thickness props['z_coord'] = z_coord props['material'] = material props['fill'] = fill_value props['chip_name'] = chip_name except Exception as ex: self._warning_search_minus_chip(layer_number, datatype, ex) result = list() for item in properties: result.append(props[item]) return tuple(result)
[docs] def is_layer_data_unique(self) -> bool: """For each layer number make sure the datatypes are unique. A layers can #have multiple datatypes. Returns: bool: True if empty dataframe, True if for each layer, there is ONLY one datatype. Otherwise, False. """ layer_nums = self.get_unique_layer_ints() if layer_nums: for num in layer_nums: mask = self.ls_df['layer'] == num search_result_num = self.ls_df[mask] if not search_result_num.datatype.is_unique: self.logger.warning( f'There WILL BE PROBLEMS since layer {num} does not have unique datatypes.' ) return False return True
def _read_csv_df(self, abs_path: str) -> None: #ASSUME that self.filename_csv_df is valid file path and name. #https://best-excel-tutorial.com/59-tips-and-tricks/485-how-to-display-a-single-quote-in-a-cell try: self.ls_df = pd.read_csv(abs_path) except BaseException as error: self.logger.warning( f'Not able to create pandas dataframe.' f'File:{abs_path} not imported. Expected a CSV formatted file.') self.logger.error(f'The exception: {error}')
[docs] def get_unique_chip_names(self) -> set: """Get a set of unique names used in the layer_stack dataframe. Returns: set: Unique names used in the layer stack used as either default or provided by user. """ names = self.ls_df['chip_name'] result = set(names.str.strip('\'')) return result
[docs] def get_unique_layer_ints(self) -> set: """Get a set of unique layer ints used in the layer_stack dataframe. Returns: set: Unique layer numbers used in the layer stack used as either default or provided by user. """ layers = self.ls_df['layer'] return set(layers.unique())
def _warning_properties(self, properties: list): """_Give warning if the properties is Args: parameters (list): _description_ """ self.logger.warning( f'The list for properties: {properties} is not a' f'subset of expected column names: {self.Col_Names}') def _warning_search(self, chip_name: str, layer_number: int, data_type: int, ex: Exception): """Give warning when the layerstack pandas table doesn't have requested chip_name, layer, and datatype information. Args: chip_name (str): Name of chip which has the layer of interest. layer_number (int): The unique layer number through out all chips. The same layer number can not be used on multiple chips. datatype (int, optional): Datatype of the layer number. """ self.logger.warning( f'\nERROR: {ex}' f'\nThere is an error searching in layer_stack dataframe using ' f'chip_name={chip_name}, layer={layer_number}, datatype={data_type}' ) def _warning_search_minus_chip(self, layer_number: int, data_type: int, ex: Exception): """Give warning when the layerstack pandas table doesn't have requested layer, and datatype information. Args: layer_number (int): The unique layer number through out all chips. The same layer number can not be used on multiple chips. datatype (int, optional): Datatype of the layer number. """ self.logger.warning( f'\nERROR: {ex}' f'\nThere is an error searching in layer_stack dataframe using ' f' layer={layer_number}, datatype={data_type}')
[docs] def layer_stack_handler_pilot_error(self): """ The handler will return None if incorrect arguments are passed. """ self.logger.error( f'User is not using LayerStackHandler.get_properties_for_chip_layer_datatype correctly.' )