def opposite_ends(self): """Shortcut for finding the opposite ends for a list of PowerTransformerEnd or Terminals""" if self.has_type("PowerTransformerEnd"): end_numbers = {a.end_number for a in self.data} if end_numbers != {1} and end_numbers != {2}: raise ValueError( f"Can't get opposite end for list with end number(s) {end_numbers}, should be either all 1 or all 2" ) opposite_end_number = 1 if list(end_numbers)[0] == 2 else 2 return self.power_transformers().power_transformer_ends( end_number=opposite_end_number) if self.has_type("Terminal"): seq_numbers = {a.sequence_number for a in self.data} if seq_numbers != {1} and seq_numbers != {2}: raise ValueError( f"Can't get opposite end for list with sequence number(s) {seq_numbers}, should be either all 1 or all 2" ) opposite_seq_number = 1 if list(seq_numbers)[0] == 2 else 2 al = self.relationship_targets(power_type="ACLineSegment", relationship_type="connectsTo") if not al: pte = self.relationship_targets( power_type="PowerTransformerEnd", relationship_type="connectsTo") return pte.opposite_ends().terminals( sequence_number=opposite_seq_number) return al.terminals(sequence_number=opposite_seq_number) else: raise WrongPowerTypeError( f"Can't get opposite ends for a list of {self.type}")
def generating_units( self, power_type: Optional[Union[str, List[str]]] = None) -> "PowerAssetList": """Shortcut for finding the associated GeneratingUnit for a list of Substations Args: power_type: type of generating unit, default is ["HydroGeneratingUnit","WindGeneratingUnit","ThermalGeneratingUnit"] """ if power_type is None: power_type = [ "HydroGeneratingUnit", "WindGeneratingUnit", "ThermalGeneratingUnit" ] if isinstance(power_type, str): power_type = [power_type] if self.has_type("Substation"): return PowerAssetList( sum([self.relationship_sources(pt) for pt in power_type], []), cognite_client=self._cognite_client, ) elif not self.data: return PowerAssetList([], cognite_client=self._cognite_client) else: raise WrongPowerTypeError( f"Can't get Generating Units [{power_type}] for a list of {self.type}" )
def non_conform_loads(self, base_voltage: Iterable = None) -> "PowerAssetList": if self.has_type("Substation"): return self.relationship_sources("NonConformLoad", base_voltage=base_voltage) elif not self.data: return PowerAssetList([], cognite_client=self._cognite_client) else: raise WrongPowerTypeError(f"Can't get NonConformLoad for a list of {self.type}")
def load_duration_curve( self, start, end="now", terminal=1, measurement_type="ThreePhaseActivePower", timeseries_type="estimated_value", granularity="1h", dropna=True, index_granularity=0.1, ) -> "pd.DataFrame": """See ACLineSegment#load_duration_curve""" if self.type not in [ "ACLineSegment", "PowerTransformerEnd", "SynchronousMachine" ]: # , "PowerTransferCorridor" raise WrongPowerTypeError( f"Can't get load duration curves dataframe for a list of {self.type}" ) if len(self.data) > 1000: raise ValueError( "Too many line segments in this list to get load duration curves" ) res_list = execute_tasks_concurrently( ACLineSegment.load_duration_curve, [(a, start, end, terminal, measurement_type, timeseries_type, granularity, dropna, index_granularity) for a in self], max_workers=10, ) return pd.concat(res_list.joined_results(), axis=1)
def busbar_sections(self, base_voltage: Iterable = None): if self.has_type("Substation"): return self.relationship_sources("BusbarSection", base_voltage=base_voltage) elif not self.data: return PowerAssetList([], cognite_client=self._cognite_client) else: raise WrongPowerTypeError(f"Can't get ConformLoads for a list of {self.type}")
def current_limits_overview(self) -> "pd.DataFrame": """See ACLineSegment#current_limits_overview""" if not self.has_type("ACLineSegment"): raise WrongPowerTypeError(f"Can't get connected current limits dataframe for a list of {self.type}") res_list = execute_tasks_concurrently( ACLineSegment.current_limits_overview, [(a,) for a in self], max_workers=10 ) return pd.concat(res_list.joined_results())
def shunt_compensators(self): if self.has_type("Substation"): return self.relationship_sources("ShuntCompensator") elif not self.data: return PowerAssetList([], cognite_client=self._cognite_client) else: raise WrongPowerTypeError( f"Can't get ShuntCompensator for a list of {self.type}")
def power_transformers(self, grid_type: Optional[str] = None) -> "PowerAssetList": """Shortcut for finding the associated PowerTransformer for a list of PowerTransformerEnd or Substation""" if self.has_type("PowerTransformerEnd"): return self.relationship_targets("PowerTransformer", grid_type=grid_type) elif self.has_type("Substation"): return self.relationship_sources("PowerTransformer", grid_type=grid_type) elif not self.data: return PowerAssetList([]) else: raise WrongPowerTypeError(f"Can't get PowerTransformers for a list of {self.type}")
def ac_line_segments(self, base_voltage: Iterable = None, grid_type: Optional[str] = None): """Shortcut for finding the associated ACLineSegment for a list of PowerTransformer, Substation or Terminal""" if self.has_type("Substation"): return self.terminals().ac_line_segments(base_voltage=base_voltage, grid_type=grid_type) elif self.has_type("Terminal"): return self.relationship_targets( "ACLineSegment", relationship_type="connectsTo", base_voltage=base_voltage, grid_type=grid_type ) elif not self.data: return PowerAssetList([], cognite_client=self._cognite_client) else: raise WrongPowerTypeError(f"Can't get ACLineSegments for a list of {self.type}")
def connected_substations( self, level: int = 1, exact: bool = False, include_lines=False, base_voltage: Iterable = None, grid_type: Optional[str] = None, ) -> "PowerAssetList": """Retrieves substations connected within level connections through ac_line_segments with base voltages within the specified range Args: level: number of connections to traverse exact: only return substations whose minimum distance is exactly level include_lines: also return ACLineSegments that make up the connections. Can not be used in combination with exact. base_voltage: only consider ACLineSegments with these base voltage grid_type: only consider ACLineSegments of this grid type """ if exact and include_lines: raise ValueError( "Can not include lines when an exact distance is requested") if not self.has_type("Substation"): raise WrongPowerTypeError( f"Can't get connected substations for a list of {self.type}") visited_substations = set(self.data) visited_lines = set() substations_at_level = self for i in range(level): ac_line_segments = substations_at_level.ac_line_segments( base_voltage=base_voltage, grid_type=grid_type) substations_at_level = ac_line_segments.substations() substations_at_level.data = [ a for a in substations_at_level.data if a not in visited_substations ] if not substations_at_level: break visited_lines.update(ac_line_segments) visited_substations.update(substations_at_level) if exact: returned_assets = substations_at_level elif include_lines: returned_assets = list(visited_substations) + list(visited_lines) else: returned_assets = visited_substations return PowerAssetList(list(returned_assets), cognite_client=self._cognite_client)
def substations(self) -> "PowerAssetList": """Shortcut for finding the associated Substations for a list of PowerTransformer, GeneratingUnit, (Non)ConformLoad, ACLineSegment or Terminal""" if (self.has_type("PowerTransformer") or self.has_type("Terminal") or self.has_type("ConformLoad") or self.has_type("NonConformLoad") or self.has_type("WindGeneratingUnit") or self.has_type("ThermalGeneratingUnit") or self.has_type("HydroGeneratingUnit") or self.has_type("BusbarSection")): return self.relationship_targets("Substation") elif self.has_type("ACLineSegment"): return self.terminals().substations() elif not self.data: return PowerAssetList([], cognite_client=self._cognite_client) else: raise WrongPowerTypeError( f"Can't get substations for a list of {self.type}")