diff --git a/eco2ai/tools/tools_cpu.py b/eco2ai/tools/tools_cpu.py index a991895..9958d48 100644 --- a/eco2ai/tools/tools_cpu.py +++ b/eco2ai/tools/tools_cpu.py @@ -244,7 +244,9 @@ def number_of_cpu(ignore_warnings=True): processor_string = dictionary['Џа®жҐбб®а(л)'] if 'Процессор(ы)' in dictionary: processor_string = dictionary['Процессор(ы)'] - cpu_num = int(re.findall(r'- (\d)\.', processor_string)[0]) + # Use regex for multi-digit CPU numbers + match = re.findall(r'- (\d+)\.', processor_string) + cpu_num = int(match[0]) if match else 1 except: if not ignore_warnings: warnings.warn( @@ -271,7 +273,26 @@ def number_of_cpu(ignore_warnings=True): # message="\nIt's impossible to determine cpu number correctly\nFor now, number of cpu devices is set to 1\n\n", # category=NoNeededLibrary # ) + try: + # Try to get the number of physical CPU packages + out = subprocess.check_output( + ["sysctl", "-n", "hw.packages"], text=True).strip() + cpu_num = int(out) + except (subprocess.CalledProcessError, ValueError): cpu_num = 0 + + if cpu_num <= 0: + try: + # Fallback: number of physical cores + out = subprocess.check_output(["sysctl", "-n", "hw.physicalcpu"], text=True).strip() + cpu_num = int(out) + except (subprocess.CalledProcessError, ValueError): + if not ignore_warnings: + warnings.warn( + "Unable to determine the number of CPU sockets on Darwin; defaulting to 1", + category=UserWarning + ) cpu_num = 1 + else: cpu_num = 1 return cpu_num diff --git a/eco2ai/tools/tools_gpu.py b/eco2ai/tools/tools_gpu.py index e4fbc19..063d6a0 100644 --- a/eco2ai/tools/tools_gpu.py +++ b/eco2ai/tools/tools_gpu.py @@ -105,14 +105,17 @@ def gpu_memory(self): """ if not self.is_gpu_available: return None - pynvml.nvmlInit() - deviceCount = pynvml.nvmlDeviceGetCount() - gpus_memory = [] - for i in range(deviceCount): - handle = pynvml.nvmlDeviceGetHandleByIndex(i) - gpus_memory.append(pynvml.nvmlDeviceGetMemoryInfo(handle)) - pynvml.nvmlShutdown() - return gpus_memory + try: + pynvml.nvmlInit() + deviceCount = pynvml.nvmlDeviceGetCount() + gpus_memory = [] + for i in range(deviceCount): + handle = pynvml.nvmlDeviceGetHandleByIndex(i) + gpus_memory.append(pynvml.nvmlDeviceGetMemoryInfo(handle)) + pynvml.nvmlShutdown() + return gpus_memory + except Exception: + return None # standardized error return def gpu_temperature(self): """ @@ -130,14 +133,17 @@ def gpu_temperature(self): """ if not self.is_gpu_available: return None - pynvml.nvmlInit() - deviceCount = pynvml.nvmlDeviceGetCount() - gpus_temps = [] - for i in range(deviceCount): - handle = pynvml.nvmlDeviceGetHandleByIndex(i) - gpus_temps.append(pynvml.nvmlDeviceGetTemperature(handle, pynvml.NVML_TEMPERATURE_GPU)) - pynvml.nvmlShutdown() - return gpus_temps + try: + pynvml.nvmlInit() + deviceCount = pynvml.nvmlDeviceGetCount() + gpus_temps = [] + for i in range(deviceCount): + handle = pynvml.nvmlDeviceGetHandleByIndex(i) + gpus_temps.append(pynvml.nvmlDeviceGetTemperature(handle, pynvml.NVML_TEMPERATURE_GPU)) + pynvml.nvmlShutdown() + return gpus_temps + except Exception: + return None def gpu_power(self): """ @@ -155,14 +161,17 @@ def gpu_power(self): """ if not self.is_gpu_available: return None - pynvml.nvmlInit() - deviceCount = pynvml.nvmlDeviceGetCount() - gpus_powers = [] - for i in range(deviceCount): - handle = pynvml.nvmlDeviceGetHandleByIndex(i) - gpus_powers.append(pynvml.nvmlDeviceGetPowerUsage(handle)) - pynvml.nvmlShutdown() - return gpus_powers + try: + pynvml.nvmlInit() + deviceCount = pynvml.nvmlDeviceGetCount() + gpus_powers = [] + for i in range(deviceCount): + handle = pynvml.nvmlDeviceGetHandleByIndex(i) + gpus_powers.append(pynvml.nvmlDeviceGetPowerUsage(handle)) + pynvml.nvmlShutdown() + return gpus_powers + except Exception: + return None def gpu_power_limit(self): """ @@ -180,18 +189,19 @@ def gpu_power_limit(self): """ if not self.is_gpu_available: return None - pynvml.nvmlInit() - deviceCount = pynvml.nvmlDeviceGetCount() - gpus_limits = [] - for i in range(deviceCount): - handle = pynvml.nvmlDeviceGetHandleByIndex(i) - gpus_limits.append(pynvml.nvmlDeviceGetEnforcedPowerLimit(handle)) - pynvml.nvmlShutdown() - return gpus_limits + try: + pynvml.nvmlInit() + deviceCount = pynvml.nvmlDeviceGetCount() + gpus_limits = [] + for i in range(deviceCount): + handle = pynvml.nvmlDeviceGetHandleByIndex(i) + gpus_limits.append(pynvml.nvmlDeviceGetEnforcedPowerLimit(handle)) + pynvml.nvmlShutdown() + return gpus_limits + except Exception: + return None - def name( - self, - ): + def name(self,): """ This class method returns GPU name if there are any GPU visible or it returns empty string. All the GPU devices are intended to be of the same model @@ -216,11 +226,14 @@ def name( pynvml.nvmlDeviceGetPowerUsage(handle) gpus_name.append(pynvml.nvmlDeviceGetName(handle)) pynvml.nvmlShutdown() - return gpus_name[0].encode().decode("UTF-8") - except: + if gpus_name: + return gpus_name[0].encode().decode("UTF-8") + else: + return "" + except Exception: return "" - def gpu_num(self): + def gpu_num(self) -> int: """ This class method returns number of visible GPU devices. Pynvml library is used. @@ -243,11 +256,11 @@ def gpu_num(self): pynvml.nvmlDeviceGetPowerUsage(handle) pynvml.nvmlShutdown() return deviceCount - except: + except Exception: return 0 -def is_gpu_available(): +def is_gpu_available() -> bool: """ This function checks if there are any available GPU devices All the GPU devices are intended to be of the same model @@ -272,7 +285,7 @@ def is_gpu_available(): gpus_powers.append(pynvml.nvmlDeviceGetPowerUsage(handle)) pynvml.nvmlShutdown() return True - except pynvml.NVMLError: + except Exception: # catch all exceptions for robustness return False @@ -298,10 +311,12 @@ def all_available_gpu(): handle = pynvml.nvmlDeviceGetHandleByIndex(i) pynvml.nvmlDeviceGetPowerUsage(handle) gpus_name.append(pynvml.nvmlDeviceGetName(handle)) - string = f"""Seeable gpu device(s): + if gpus_name: + string = f"""Seeable gpu device(s): {gpus_name[0].decode("UTF-8")}: {deviceCount} device(s)""" - print(string) + print(string) + else: + print("There is no any available gpu device(s)") pynvml.nvmlShutdown() - except: + except Exception: print("There is no any available gpu device(s)") - \ No newline at end of file diff --git a/eco2ai/tools/tools_ram.py b/eco2ai/tools/tools_ram.py index d985b71..b7bba5c 100644 --- a/eco2ai/tools/tools_ram.py +++ b/eco2ai/tools/tools_ram.py @@ -80,7 +80,7 @@ def _get_memory_used(self,): return memory_percent * total_memory / 100 - def calculate_consumption(self): + def calculate_consumption(self) -> float: """ This class method calculates RAM power consumption. @@ -97,7 +97,8 @@ def calculate_consumption(self): time_period = time.time() - self._start self._start = time.time() consumption = self._get_memory_used() * (3 / 8) * time_period / FROM_WATTs_TO_kWATTh - + if consumption < 0: # ensure no negative values + consumption = 0 self._consumption += consumption # print(self._consumption) return consumption \ No newline at end of file diff --git a/eco2ai/utils.py b/eco2ai/utils.py index 1d720f9..fce879a 100644 --- a/eco2ai/utils.py +++ b/eco2ai/utils.py @@ -20,7 +20,7 @@ class NotNeededExtensionError(Exception): pass -def available_devices(): +def available_devices() -> None: """ This function prints all the available CPU & GPU devices @@ -118,11 +118,11 @@ def define_carbon_index( carbon_index_table_name = resource_stream('eco2ai', 'data/carbon_index.csv').name if alpha_2_code is None: try: - ip_dict = eval(requests.get("https://ipinfo.io/").content) + ip_dict = json.loads(requests.get("https://ipinfo.io/").content) # safer than eval except: - ip_dict = eval(requests.get("https://ipinfo.io/").content.decode('ascii')) - country = ip_dict['country'] - region = ip_dict['region'] + ip_dict = json.loads(requests.get("https://ipinfo.io/").content.decode('ascii')) + country = ip_dict.get('country', None) + region = ip_dict.get('region', None) else: country = alpha_2_code if emission_level is not None: @@ -507,6 +507,8 @@ def summary( if not filename.endswith('.csv'): raise NotNeededExtensionError('File need to be with extension \'.csv\'') df = pd.read_csv(filename) + if df.empty: + raise ValueError("CSV file is empty, cannot summarize.") projects = np.unique(df['project_name'].values) summary_data = [] columns = [