Coverage for pyrc\tools\functions.py: 56%
66 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# ------------------------------------------------------------------------------
8from __future__ import annotations
10from datetime import datetime, timedelta
11from typing import Any
13import numpy as np
14from sympy import Basic, Matrix, diag, latex, sympify
17def add_leading_underscore(string: str = "") -> str:
18 """
19 Adds a leading underscore if not already existing and string is not None or empty.
21 Parameters
22 ----------
23 string : str
24 The string to check.
26 Returns
27 -------
28 str :
29 Checked string.
30 """
31 if string is not None and string != "":
32 if not string.startswith("_"):
33 string = f"_{string}"
34 else:
35 string = ""
36 return string
39def sympy_matrix_to_latex(matrix):
40 """
41 Return a LaTeX representation of a sympy expression.
43 Parameters
44 ----------
45 matrix : sympy.Matrix
46 Expression or matrix to convert to LaTeX.
48 Returns
49 -------
50 str
51 LaTeX representation of expr.
52 """
54 diag_elements = matrix.diagonal()
55 matrix_no_diag = matrix - diag(*diag_elements)
57 matrix_latex = latex(matrix_no_diag)
58 diag_latex = latex(Matrix(diag_elements).reshape(len(diag_elements), 1))
60 return f"{matrix_latex} + \\mathrm{{diag}}\\left({diag_latex}\\right)"
63def save_as_tex(filename, latex_str):
64 if isinstance(latex_str, list):
65 latex_str = "\n\n".join(latex_str)
66 with open(filename, "w") as f:
67 f.write(
68 rf"""\documentclass[border=3mm]{{standalone}}
69\usepackage{{amsmath}}
70\begin{{document}}
71$ {latex_str} $
72\end{{document}}"""
73 )
76def sympy_to_tex(matrix, file):
77 save_as_tex(file, sympy_matrix_to_latex(matrix))
80# def sympy_sparse_matrix_to_latex(M, filename, fontsize="20pt"):
81# latex_str = sparse_matrix_to_nicematrix_latex(M)
82# doc = rf"""\documentclass[border=3mm]{{standalone}}
83# \usepackage{{amsmath}}
84# \usepackage{{nicematrix}}
85# \usepackage{{lmodern}}
86# \usepackage{{anyfontsize}}
87#
88# \NiceMatrixOptions{{cell-space-limits=2pt}}
89#
90# \begin{{document}}
91# \fontsize{{{fontsize}}}{{{fontsize}}}\selectfont
92# \[
93# {latex_str}
94# \]
95# \end{{document}}
96# """
97# with open(filename, "w") as f:
98# f.write(doc)
101def is_set(value):
102 """
103 Returns True if the value is already set (by value or symbol) and False if it is np.nan.
105 Parameters
106 ----------
107 value : any
108 The value to check.
110 Returns
111 -------
112 bool :
113 True if the resistance value is already set and False if it is np.nan.
114 """
115 if isinstance(value, Basic):
116 return True
117 if value is None:
118 return False
119 if isinstance(value, np.ndarray):
120 return value.size > 0
121 if isinstance(value, (list, tuple)):
122 return len(value) > 0
123 if np.isnan(value):
124 return False
125 return True
128def get_nested_attribute(obj, attribute_path: str):
129 """
130 Get nested attribute value from object using dot notation.
132 Parameters
133 ----------
134 obj : object
135 Object to get attribute from
136 attribute_path : str
137 Dot-separated attribute path (e.g., 'material.name')
139 Returns
140 -------
141 Any
142 Value of the attribute
143 """
144 attributes = attribute_path.split(".")
145 value = obj
146 for attr in attributes:
147 value = getattr(value, attr)
148 return value
151def contains_symbol(expr: float | int | np.ndarray | np.number | Any) -> bool:
152 """Check whether any sympy symbol appears in an expression, array, or scalar.
154 Parameters
155 ----------
156 expr : float | int | np.ndarray | np.number | Any
157 Expression, numpy array, list, scalar, or np.nan to check.
159 Returns
160 -------
161 bool :
162 True if any sympy symbol is found anywhere in expr.
163 """
164 if isinstance(expr, np.ndarray):
165 return any(contains_symbol(e) for e in expr.flat)
166 if isinstance(expr, (list, tuple)):
167 return any(contains_symbol(e) for e in expr)
168 try:
169 return bool(sympify(expr).free_symbols)
170 except Exception:
171 return False
174def check_type(nodes, type1, type2) -> bool:
175 return (isinstance(nodes[0], type1) and isinstance(nodes[1], type2)) or (
176 isinstance(nodes[1], type1) and isinstance(nodes[0], type2)
177 )
180def return_type(nodes: list, type1, check_value: list | Any = None):
181 if check_value is None:
182 check_value = nodes
183 assert len(check_value) == len(nodes)
184 if isinstance(check_value[0], type1):
185 return tuple(nodes)
186 else:
187 return nodes[1], nodes[0]
190def subtract_seconds_from_string(date_string: str, seconds: int | float) -> str:
191 dt = datetime.fromisoformat(date_string)
192 result_dt = dt - timedelta(seconds=seconds)
193 return result_dt.isoformat()
196def fill_none(list1, list2):
197 return [b if a is None else a for a, b in zip(list1, list2)]