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