Coverage for pyrc\tools\science.py: 41%
73 statements
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-29 15:57 +0200
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-29 15:57 +0200
1# -------------------------------------------------------------------------------
2# Copyright (C) 2026 Joel Kimmich, Tim Jourdan
3# ------------------------------------------------------------------------------
4# License
5# This file is part of PyRC, distributed under GPL-3.0-or-later.
6# ------------------------------------------------------------------------------
8import multiprocessing
9import os
11import numpy as np
12from psutil import virtual_memory
13from sympy import SparseMatrix, diff
15os.environ["SYMPY_CACHE_SIZE"] = "10000000000"
18def get_free_ram_gb():
19 return get_free_ram() / (1024**3)
22def get_free_ram():
23 return virtual_memory().available
26def kelvin_to_celsius(kelvin):
27 return kelvin - 273.15
30def celsius_to_kelvin(celsius):
31 return celsius + 273.15
34def build_jacobian(terms: list, variables: list, involved_symbols: list, num_cores: int = None) -> SparseMatrix:
35 """
36 creates the jacobian matrix using all terms and their involved symbols.
38 Parameters
39 ----------
40 terms : list
41 All terms in a list.
42 variables : list
43 All variables of all terms in a list.
44 involved_symbols : list
45 Lists with the symbols the derivative must be created for each term in a list.
46 So for term[0] there are only the variables involved_symbols[0] relevant.
47 It is used to make the calculation faster and set 0 to all other places in the jacobian matrix where the
48 symbols are not involved.
49 num_cores : int, optional
50 The number of cores that should be used to calculate the jacobian matrix.
51 If None, the maximum number of cores except one is used.
52 If 1 or smaller, no parallel computing is used.
54 Returns
55 -------
56 Any :
57 The jacobian matrix with symbols.
58 """
60 # Define batch size for efficiency
61 if num_cores is None:
62 num_cores = multiprocessing.cpu_count() - 1
64 if num_cores <= 1:
65 jacobian_rows = compute_jacobian_batch(list(range(0, len(terms))), terms, involved_symbols, variables)
66 else:
67 batch_size = max(1, len(terms) // (num_cores * 3)) # Adjust batch size based on problem size
68 batches = [list(range(i, min(i + batch_size, len(terms)))) for i in range(0, len(terms), batch_size)]
70 # Prepare arguments for starmap
71 batch_args = [(batch, terms, involved_symbols, variables) for batch in batches]
73 # Parallel execution with batching using starmap
74 with multiprocessing.Pool(processes=num_cores) as pool:
75 jacobian_batches = pool.starmap(compute_jacobian_batch, batch_args)
77 # Flatten the result and convert to SparseMatrix
78 jacobian_rows = [row for batch in jacobian_batches for row in batch]
79 jacobian = SparseMatrix(jacobian_rows)
81 return jacobian
84def is_numeric(value):
85 """
86 Checks if value is a numeric value.
88 Parameters
89 ----------
90 value : Any
91 Value to check.
93 Returns
94 -------
95 bool
96 True if value is numeric, False otherwise
97 """
98 list_of_numerics = [float, int, np.number] # add numeric data types here if necessary
99 for numeric in list_of_numerics:
100 if isinstance(value, numeric):
101 return True
102 return False
105def round_valid(number: float | int, valid_digits: int, return_str: bool = False):
106 if np.isnan(number):
107 if return_str:
108 return "NaN"
109 return np.nan
110 if number == np.inf:
111 if return_str:
112 return "Infinity"
113 return np.inf
114 if number == -np.inf:
115 if return_str:
116 return "-Infinity"
117 return -np.inf
118 if int(number) == 0:
119 # number < 1
120 if number == 0:
121 if return_str:
122 return "0"
123 return 0
124 counter = 0
125 while int(number) == 0:
126 number = number * 10
127 counter += 1
128 if return_str:
129 format_str = f"{{:{counter + 1 + valid_digits}.{valid_digits + counter - 1}f}}"
130 return format_str.format(np.round(number, valid_digits) / 10**counter)
131 return np.round(np.round(number, valid_digits) / 10**counter, counter + valid_digits - 1)
132 else:
133 ten_digits = int(np.log10(abs(number))) + 1
134 if ten_digits >= valid_digits:
135 number = int(number)
136 if return_str:
137 return f"{np.round(number, valid_digits - ten_digits)}"
138 return np.round(number, valid_digits - ten_digits)
141# Function to compute a batch of Jacobian rows
142def compute_jacobian_batch(_batch, _terms, _dependent_variables, _all_variables):
143 batch_result = []
144 for i in _batch:
145 row = [diff(_terms[i], v) if v in _dependent_variables[i] else 0 for v in _all_variables]
146 batch_result.append(row)
147 return batch_result
150def cm_to_inch(value):
151 return value / 2.54