From 20f34de6e1a494b5caddd7bcbc5b827a19c51f74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20H=C3=B6lscher?= Date: Tue, 11 Nov 2025 07:57:47 +0100 Subject: [PATCH 01/22] Add Newton-Damping for NL solver --- plutho/mesh/__init__.py | 2 +- plutho/simulations/nonlinear/piezo_time.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/plutho/mesh/__init__.py b/plutho/mesh/__init__.py index 04edc55..5d451e1 100644 --- a/plutho/mesh/__init__.py +++ b/plutho/mesh/__init__.py @@ -1 +1 @@ -from .mesh import Mesh +from .mesh import Mesh, MeshData diff --git a/plutho/simulations/nonlinear/piezo_time.py b/plutho/simulations/nonlinear/piezo_time.py index 068dcbc..c304735 100644 --- a/plutho/simulations/nonlinear/piezo_time.py +++ b/plutho/simulations/nonlinear/piezo_time.py @@ -65,6 +65,7 @@ def simulate( beta: float, tolerance: float = 1e-11, max_iter: int = 300, + newton_damping: float = 1, u_start: Union[npt.NDArray, None] = None ): """Simulates the nonlinear piezo time system. @@ -182,7 +183,7 @@ def residual(next_u, current_u, v, a, f): (a1*m+a4*c+tangent_matrix), residual(u_i, current_u, current_v, current_a, f) ) - u_i_next = u_i - delta_u + u_i_next = u_i - delta_u * newton_damping # Check for convergence norm = scipy.linalg.norm(residual( From 1d42b033d347ce0f0a134c02a6757241e6ada38b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20H=C3=B6lscher?= Date: Tue, 11 Nov 2025 07:58:33 +0100 Subject: [PATCH 02/22] Add tukey window for NL script --- scripts/example_nonlinear.py | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/scripts/example_nonlinear.py b/scripts/example_nonlinear.py index fa30763..345fca6 100644 --- a/scripts/example_nonlinear.py +++ b/scripts/example_nonlinear.py @@ -65,7 +65,8 @@ def create_sinusoidal_excitation( amplitude, delta_t, number_of_time_steps, - frequency + frequency, + rise_steps = 0 ): """Creates a sinusoidal excitation array. @@ -76,8 +77,12 @@ def create_sinusoidal_excitation( this is the same as for the simulation frequency: Frequency of the sin function. """ - time_steps = np.arange(number_of_time_steps)*delta_t - return amplitude*np.sin(2*np.pi*frequency*time_steps) + time = np.arange(number_of_time_steps)*delta_t + window = signal.windows.tukey( + number_of_time_steps, + rise_steps/number_of_time_steps*2 + ) + return window*amplitude*np.sin(2*np.pi*frequency*time) def simulate_nl_time(CWD, mesh, delta_t, number_of_time_steps): @@ -87,6 +92,9 @@ def simulate_nl_time(CWD, mesh, delta_t, number_of_time_steps): ZETA = 10 AMPLITUDE = 10 FREQUENCY = 2.07e6 + NEWTON_DAMPING = 0.25 + MAX_ITER = 1000 + TOLERANCE = 5e-9 nonlinearity = plutho.Nonlinearity() nonlinearity.set_cubic_rayleigh(ZETA) @@ -112,6 +120,7 @@ def simulate_nl_time(CWD, mesh, delta_t, number_of_time_steps): number_of_time_steps=number_of_time_steps, frequency=FREQUENCY ) + sim.add_dirichlet_bc( plutho.FieldType.PHI, "Electrode", @@ -130,8 +139,9 @@ def simulate_nl_time(CWD, mesh, delta_t, number_of_time_steps): number_of_time_steps, GAMMA, BETA, - tolerance=5e-9, - max_iter=40 + tolerance=TOLERANCE, + max_iter=MAX_ITER, + newton_damping=NEWTON_DAMPING ) sim.calculate_charge("Electrode") @@ -139,6 +149,8 @@ def simulate_nl_time(CWD, mesh, delta_t, number_of_time_steps): if not os.path.isdir(CWD): os.makedirs(CWD) + print(u[:2*node_count]) + u_file = os.path.join(CWD, "u.npy") q_file = os.path.join(CWD, "q.npy") np.save(u_file, sim.u) @@ -159,6 +171,8 @@ def plot_displacement_spectrum( frequencies = np.fft.fftfreq(number_of_time_steps, delta_t) + plt.plot(np.arange(number_of_time_steps)*delta_t, u_r) + plt.show() plt.plot(frequencies, np.abs(U_r_jw)) plt.grid() plt.show() @@ -186,17 +200,17 @@ def plot_displacement_spectrum( ) mesh = plutho.Mesh(mesh_file, element_order=1) - DELTA_T = 1e-9 + DELTA_T = 1e-8 NUMBER_OF_TIME_STEPS = 20000 - nl_time_sim_name = "nonlinear_time_dep_sim_20k_1e-9" + nl_time_sim_name = "nonlinear_20000_1e8_10" nl_time_wd = os.path.join(CWD, nl_time_sim_name) ## Simulate - if True: + if False: # simulate_nonlinear_stationary(CWD) simulate_nl_time(nl_time_wd, mesh, DELTA_T, NUMBER_OF_TIME_STEPS) ## Plot - if False: + if True: plot_displacement_spectrum(nl_time_wd, DELTA_T, NUMBER_OF_TIME_STEPS) From e5c1fd4e77150754f4628dfca77fb60c3c6cda26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20H=C3=B6lscher?= Date: Tue, 11 Nov 2025 09:02:16 +0100 Subject: [PATCH 03/22] Swap field indices in NL solver --- plutho/simulations/nonlinear/piezo_time.py | 37 ++++++++++++---------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/plutho/simulations/nonlinear/piezo_time.py b/plutho/simulations/nonlinear/piezo_time.py index c304735..fd3b8b4 100644 --- a/plutho/simulations/nonlinear/piezo_time.py +++ b/plutho/simulations/nonlinear/piezo_time.py @@ -98,17 +98,17 @@ def simulate( # Init arrays # Displacement u which is calculated u = np.zeros( - (3*number_of_nodes, number_of_time_steps), + (number_of_time_steps, 3*number_of_nodes), dtype=np.float64 ) # Displacement u derived after t (du/dt) v = np.zeros( - (3*number_of_nodes, number_of_time_steps), + (number_of_time_steps, 3*number_of_nodes), dtype=np.float64 ) # v derived after u (d^2u/dt^2) a = np.zeros( - (3*number_of_nodes, number_of_time_steps), + (number_of_time_steps, 3*number_of_nodes), dtype=np.float64 ) @@ -135,20 +135,20 @@ def residual(next_u, current_u, v, a, f): ) if u_start is not None: - u[:, 0] = u_start + u[0, :] = u_start print("Starting nonlinear time domain simulation") for time_index in range(number_of_time_steps-1): # Calculate load vector f = self._get_load_vector( dirichlet_nodes, - dirichlet_values[:, time_index+1] + dirichlet_values[:, time_index+1] # TODO Swap indices ) # Values of the current time step - current_u = u[:, time_index] - current_v = v[:, time_index] - current_a = a[:, time_index] + current_u = u[time_index, :] + current_v = v[time_index, :] + current_a = a[time_index, :] # Current iteration value of u of the next time step # as a start value this is set to the last converged u of the last @@ -191,10 +191,10 @@ def residual(next_u, current_u, v, a, f): )) if norm < tolerance: - print( - f"Newton converged at time step {time_index} " - f"after {i+1} iteration(s)" - ) + # print( + # f"Newton converged at time step {time_index} " + # f"after {i+1} iteration(s)" + # ) # print(u_i_next) next_u = u_i_next self.converged = True @@ -210,8 +210,8 @@ def residual(next_u, current_u, v, a, f): u_i = u_i_next if not self.converged: print( - "Newton did not converge.. Choosing best value: " - f"{best_norm}" + f"Newton did not converge at time index: {time_index}. " + f"Choosing best value: {best_norm}" ) next_u = best_u_i else: @@ -219,19 +219,22 @@ def residual(next_u, current_u, v, a, f): next_u = best_u_i # Calculate next v and a - a[:, time_index+1] = ( + a[time_index+1, :] = ( a1*(next_u-current_u) - a2*current_v - a3*current_a ) - v[:, time_index+1] = ( + v[time_index+1, :] = ( a4*(next_u-current_u) + a5*current_v + a6*current_a ) # Set u array value - u[:, time_index+1] = next_u + u[time_index+1, :] = next_u + + if (time_index > 0 and time_index % 100 == 0): + print(f"Time index {time_index} finished.") self.u = u From f08385f354626789757794e52c0f7c36f0512c1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20H=C3=B6lscher?= Date: Thu, 13 Nov 2025 23:55:18 +0100 Subject: [PATCH 04/22] Add Harmonic Balancing Solver --- plutho/enums.py | 1 + plutho/simulations/nonlinear/__init__.py | 1 + plutho/simulations/nonlinear/piezo_hb.py | 579 +++++++---------------- scripts/example_hb.py | 163 +++++++ 4 files changed, 326 insertions(+), 418 deletions(-) create mode 100644 scripts/example_hb.py diff --git a/plutho/enums.py b/plutho/enums.py index 3072d7a..d210b47 100644 --- a/plutho/enums.py +++ b/plutho/enums.py @@ -18,6 +18,7 @@ class SolverType(Enum): PiezoTime = "PiezoTime", ThermoPiezoTime = "ThermoPiezoTime", ThermoTime = "ThermoTime" + PiezoHB = "PiezoHarmonicBalance" class NonlinearType(Enum): diff --git a/plutho/simulations/nonlinear/__init__.py b/plutho/simulations/nonlinear/__init__.py index 41b601f..817f2f1 100644 --- a/plutho/simulations/nonlinear/__init__.py +++ b/plutho/simulations/nonlinear/__init__.py @@ -1,2 +1,3 @@ from .base import * from .piezo_time import * +from .piezo_hb import * diff --git a/plutho/simulations/nonlinear/piezo_hb.py b/plutho/simulations/nonlinear/piezo_hb.py index 963a904..33668d1 100644 --- a/plutho/simulations/nonlinear/piezo_hb.py +++ b/plutho/simulations/nonlinear/piezo_hb.py @@ -2,352 +2,208 @@ harmonic balancing method.""" # Python standard libraries -from typing import Union, Tuple, Callable +from typing import Tuple # Third party libraries import numpy as np import numpy.typing as npt -import scipy from scipy import sparse -from scipy.sparse import linalg +import scipy.sparse.linalg as slin +import scipy # Local libraries -from .base import assemble, NonlinearType -from ...mesh.mesh import MeshData -from plutho.materials import MaterialManager +from ...enums import SolverType +from .base import assemble, Nonlinearity +from ...mesh.mesh import Mesh +from ..solver import FEMSolver + +__all__ = [ + "NLPiezoHB" +] -class NLPiezoHB: + +class NLPiezoHB(FEMSolver): """Implementes a nonlinear FEM harmonic balancing simulation. """ - # Simulation parameters - mesh_data: MeshData - material_manager: MaterialManager - - # Boundary conditions - dirichlet_nodes: npt.NDArray - dirichlet_values: npt.NDArray + # Nonlinear simulation + nonlinearity: Nonlinearity - # FEM matrices - k: sparse.lil_array - c: sparse.lil_array - m: sparse.lil_array - lu: sparse.lil_array + # Harmonic balancing + hb_order: int - # Resulting fields - u: npt.NDArray def __init__( self, - mesh_data: MeshData, - material_manager: MaterialManager, - harmonic_order: int + simulation_name: str, + mesh: Mesh, + nonlinearity: Nonlinearity, + hb_order: int ): - self.mesh_data = mesh_data - self.material_manager = material_manager - self.harmonic_order = harmonic_order - self.dirichlet_nodes = np.array([]) - self.dirichlet_valuse = np.array([]) + super().__init__(simulation_name, mesh) - def assemble( - self, - nonlinear_order: int, - nonlinear_type: NonlinearType, - **kwargs - ): - """Redirect to general nonlinear assembly function. + self.solver_type = SolverType.PiezoHB + + self.nonlinearity = nonlinearity + self.nonlinearity.set_mesh_data(self.mesh_data, self.node_points) + self.hb_order = hb_order + + def assemble(self): + """Redirect to general nonlinear assembly function""" + self.material_manager.initialize_materials() - Parameters: - nonlinear_order: Order of the nonlinearity. - nonlinear_type: Type of the nonlinear material. - **kwargs: Parameters for the nonlinear material. - """ - # Get the default FEM matrices m, c, k = assemble( self.mesh_data, - self.material_manager, + self.material_manager ) - self.nonlinear_order = nonlinear_order - self.m = m self.c = c self.k = k - raise NotImplementedError("Nonlinear assembly not implemented for HB") - - def solve_linear(self): - """Solves the linear problem Ku=f (ln is not used). - """ - # Apply boundary conditions - _, _, k = NLPiezoHB.apply_dirichlet_bc( - None, - None, - self.k.copy(), - self.dirichlet_nodes - ) - - f = self.get_load_vector( - self.dirichlet_nodes, - self.dirichlet_values - ) - - # Calculate u using phi as load vector - self.u = linalg.spsolve( - k, - f - ).todense() + self.nonlinearity.assemble(m, c, k) - def solve_newton( + def simulate( self, - angular_frequency: float, - tolerance: float = 1e-10, - max_iter: int = 100, - u_start: Union[npt.NDArray, None] = None, - alpha: float = 1, - load_factor: float = 1 + frequencies: npt.NDArray, + tolerance: float = 1e-11, + max_iter: int = 300, ): - """Solves the system of nonlinear equations using the Newton-Raphson - method. Saves the result in self.u - - Parameters: - angular_frequency: Angualr frequency at which the simulation is - done. - tolerance: Upper bound for the residuum. Iteration stops when the - residuum is lower than tolerance. - max_iter: Maximum number of iteration when the tolerance is not - met. Then the best solution (lowest residuum) is returned - u_start: Initial guess for the nonlinear problem. If it is None - a linear solution is calculated and used as a guess for - nonlinear u. - alpha: Damping parameter for updating the guess for u. Can be used - to improve convergence. Choose a value between 0 and 1. - load_factor: Multiplied with the load vector. Used to apply less - than than the full excitation. Could improve conversion of the - algorithm. Choose a value between 0 and 1. - """ + dirichlet_nodes = np.array(self.dirichlet_nodes) + dirichlet_values = np.array(self.dirichlet_values) number_of_nodes = len(self.mesh_data.nodes) - # Get FEM matrices - fem_m = self.m.copy() - fem_c = self.c.copy() - fem_k = self.k.copy() - fem_lu = self.lu.copy() - - # Create the harmonic balancing matrices from the FEM matrices - hb_m, hb_c, hb_k = self.get_harmonic_balancing_matrices( - fem_m, - fem_c, - fem_k, - angular_frequency - ) + m = self.m.copy() + c = self.c.copy() + k = self.k.copy() - # Apply boundary conditions - hb_m, hb_c, hb_k = NLPiezoHB.apply_dirichlet_bc( - hb_m, - hb_c, - hb_k, - self.dirichlet_nodes + # Apply dirichlet bc to matrices + m, c, k = self._apply_dirichlet_bc( + m, + c, + k, + dirichlet_nodes ) + self.nonlinearity.apply_dirichlet_bc(dirichlet_nodes) - f = self.get_load_vector( - self.dirichlet_nodes, - self.dirichlet_values + # Prepare array + u = np.zeros( + (len(frequencies), 3*self.hb_order*number_of_nodes), + dtype=np.complex128 ) - # Set start value - if u_start is None: - # If no start value is given calculate one using a linear - # simulation - current_u = linalg.spsolve( - ( - hb_m - + hb_c - + hb_k - ), - load_factor*f + print("Starting harmonic balancing simulation") + for frequency_index, frequency in enumerate(frequencies): + f = self._get_load_vector( + dirichlet_nodes, + dirichlet_values[:, frequency_index] ) - else: - current_u = u_start - - # Residual of the Newton algorithm - def residual(u, nonlinear_force): - return ( - hb_m@u - + hb_c@u - + hb_k@u - + nonlinear_force - - load_factor*f - ) - - # Nonlinear forces based on starting field guess - nonlinear_force = self.calculate_nonlinear_force( - current_u, - fem_lu, - number_of_nodes - ) - # best_field tracks the field with the best norm - # best norm tracks the value of the norm itself - # Calculate the first norms based on the starting field guess - best_field = current_u.copy() - best_norm = np.linalg.norm(residual(best_field, nonlinear_force)) - - # Check if the initial value already suffices the tolerance condition - #if best_norm < tolerance: - # print(f"Initial value is already best {best_norm}") - # self.u = best_field - # return - - # Run Newton method - for iteration_count in range(max_iter): - if iteration_count > 0: - # Does not need to be updated at step 0 - nonlinear_force = self.calculate_nonlinear_force( - current_u, - fem_lu, - number_of_nodes + # Get initial value + if frequency_index > 0: + u_i = u[frequency_index-1, :] + else: + u_i = u[frequency_index, :] + + # Check if initial value is already sufficient + best_norm = scipy.linalg.norm(self.residual( + m, c, k, u_i, f, frequency + )) + if best_norm < tolerance: + u[frequency_index, :] = u_i + continue + best_u_i = u_i + + # Newton iteration + converged = False + for i in range(max_iter): + # Calculate next guess for u using tangent matrix + tangent_matrix = self.tangent_matrix( + u_i, + m, + c, + k, + frequency ) - - # Calculate tangential stiffness matrix - k_tangent = self.calculate_tangent_matrix_analytical( - current_u, - hb_m, - hb_c, - hb_k - ) - - # Solve for displacement increment - delta_u = linalg.spsolve( - k_tangent, - residual(current_u, nonlinear_force) - ) - - # Update step - next_u = current_u - alpha * delta_u - - # Check for convergence - norm = scipy.linalg.norm(residual(next_u, nonlinear_force)) - if norm < tolerance: - print( - f"Newton found solution after {iteration_count+1} " - f"steps with residual {norm}" + delta_u = slin.spsolve( + tangent_matrix.tocsc(), + self.residual(m, c, k, u_i, f, frequency) ) - self.u = next_u - return - elif norm < best_norm: - best_norm = norm - best_field = next_u.copy() - - if iteration_count % 100 == 0 and iteration_count > 0: - print("Iteration:", iteration_count) - - # Update for next iteration - current_u = next_u - - print("Simulation did not converge") - print("Error from best iteration:", best_norm) - - self.u = best_field - - def calculate_nonlinear_force( - self, - u: npt.NDArray, - fem_lu: sparse.lil_array, - number_of_nodes: int - ) -> npt.NDArray: - """Calculates the forces due to the nonlinearity. - - Parameters: - u: Current solution vector. - fem_lu: FEM nonlinearity matrix. - number_of_nodes: Number of nodes of the mesh. - - Returns: - Vector of nonlinear forces. - """ - # TODO Currently only for third order nonlinearity - - u_c = u[::2] - u_s = u[1::2] - - lu_c = fem_lu.dot( - 3/4*u_c**3+3/4*np.multiply(u_c, u_s**2) - ) - lu_s = fem_lu.dot( - 3/4*u_s**3+3/4*np.multiply(u_s, u_c**2) - ) + u_i_next = u_i - delta_u + + # Check for convergence + norm = scipy.linalg.norm(self.residual( + m, c, k, u_i_next, f, frequency + )) + if norm < tolerance: + # Newton converged + u[frequency_index, :] = u_i_next + converged = True + print(f"Frequency step {frequency_index} converged after " + f"iteration {i+1}" + ) + break + elif norm < best_norm: + best_norm = norm + best_u_i = u_i_next + + if i % 100 == 0 and i > 0: + print("Iteration:", i) + + if not converged: + print("Newton did not converge at frequency index " + f"{frequency_index}. Choosing best value: {best_norm}") + u[frequency_index, :] = best_u_i + + self.u = u + + def residual(self, m, c, k, u, f, frequency): + number_of_nodes = len(self.mesh_data.nodes) + res = np.zeros(self.hb_order*3*number_of_nodes, dtype=np.complex128) + angular_frequency = 2*np.pi*frequency + + for n in range(self.hb_order): + u_n = u[n*3*number_of_nodes:(n+1)*3*number_of_nodes] + f_n = f[n*3*number_of_nodes:(n+1)*3*number_of_nodes] + res[n*3*number_of_nodes:(n+1)*3*number_of_nodes] = ( + - angular_frequency**2*(n+1)**2*m@u_n + + 1j*angular_frequency*(n+1)*c@u_n + + k@u_n + - f_n + ) - nonlinear_force = np.zeros(2*3*number_of_nodes) - nonlinear_force[::2] = lu_c - nonlinear_force[1::2] = lu_s + # Add nonlinearity + if self.hb_order >= 3: + u_3 = u[2*3*number_of_nodes:3*3*number_of_nodes] + res[2*3*number_of_nodes:3*3*number_of_nodes] += \ + self.nonlinearity.evaluate_force_vector(u_3, m, c, k) - """ - nonlinear_force = np.zeros(2*3*number_of_nodes) - for i in range(3*number_of_nodes): - a = u[2*i] - b = u[2*i+1] - nonlinear_force[2*i:2*i+2] = fem_lu[i,i]*np.array([ - 3/4*(a**3+a*b**2), - 3/4*(a**2*b+b**3) - ]) - """ + return res - # Add dirichlet boundary conditions - nonlinear_force[self.dirichlet_nodes] = 0 + def tangent_matrix(self, u, m, c, k, frequency): + angular_frequency = 2*np.pi*frequency + number_of_nodes = len(self.mesh_data.nodes) - return nonlinear_force + blocks = [] + for n in range(self.hb_order): + u_n = u[n*3*number_of_nodes:(n+1)*3*number_of_nodes] + blocks.append( + - angular_frequency**2*(n+1)**2*m + + 1j*angular_frequency*(n+1)*c + + k + + self.nonlinearity.evaluate_jacobian(u_n, m, c, k) + ) - def get_harmonic_balancing_matrices( - self, - fem_m: sparse.lil_array, - fem_c: sparse.lil_array, - fem_k: sparse.lil_array, - angular_frequency: float - ) -> Tuple[sparse.lil_array, sparse.lil_array, sparse.lil_array]: - """Calculates the harmonic balancing matrices from the FEM matrices. - The matrices are getting bigger by a factor of (2*harmonic_order)**2 + # Add nonlinearity + if self.hb_order >= 3: + u_3 = u[2*3*number_of_nodes:3*3*number_of_nodes] + blocks[2] += self.nonlinearity.evaluate_jacobian(u_3, m, c, k) - Parameters: - fem_m: FEM mass matrix. - fem_c: FEM damping matrix. - fem_k: FEM stiffness matrix. + return sparse.block_diag(blocks) - Returns: - Tuple of HB mass matrix, HB damping matrix and HB stiffness - matrix. - """ - harmonic_order = self.harmonic_order - - # Create harmonic balance derivative matrices - nabla_m = np.zeros(shape=(2*harmonic_order, 2*harmonic_order)) - nabla_c = np.zeros(shape=(2*harmonic_order, 2*harmonic_order)) - nabla_k = np.zeros(shape=(2*harmonic_order, 2*harmonic_order)) - for i in range(harmonic_order): - k = i + 1 - nabla_m[2*i:2*i+2, 2*i:2*i+2] = np.array([ - [-(angular_frequency**2*k**2), 0], - [0, -(angular_frequency**2*k**2)] - ]) - nabla_c[2*i:2*i+2, 2*i:2*i+2] = np.array([ - [0, angular_frequency*k], - [-(angular_frequency*k), 0] - ]) - nabla_k[2*i:2*i+2, 2*i:2*i+2] = np.array([ - [1, 0], - [0, 1] - ]) - - # Apply HB derivative matrices on FEM matrices - fem_m = sparse.kron(fem_m, nabla_m, format='lil').tolil() - fem_c = sparse.kron(fem_c, nabla_c, format='lil').tolil() - fem_k = sparse.kron(fem_k, nabla_k, format='lil').tolil() - - return fem_m, fem_c, fem_k - - def get_load_vector( + def _get_load_vector( self, nodes: npt.NDArray, - values: npt.NDArray, + values: npt.NDArray ) -> npt.NDArray: """Calculates the load vector (right hand side) vector for the simulation. @@ -363,105 +219,15 @@ def get_load_vector( # Can be initialized to 0 because external load and volume # charge density is 0. - f = np.zeros( - 3*number_of_nodes*2*self.harmonic_order, - dtype=np.float64 - ) + f = np.zeros(self.hb_order*3*number_of_nodes, dtype=np.float64) - for node, value in zip(nodes, values): - f[node] = value + # Set dirichlet bc + f[nodes] = values return f - def calculate_tangent_matrix_analytical( + def _apply_dirichlet_bc( self, - u: npt.NDArray, - hb_m: sparse.lil_array, - hb_c: sparse.lil_array, - hb_k: sparse.lil_array - ) -> sparse.csc_array: - """Calculates the tangent matrix using a priori analytical - calculations. - - Parameters: - u: Solution vector containing mechanical and electrical field - values. - hb_m: Harmonic balancing mass matrix. - hb_c: Harmonic balancing damping matrix. - hb_k: Harmonic balancing stiffness matrix. - - Returns: - The tangent stiffness matrix. - """ - # TODO Only valid for harmonic_order = 1 and nonlinearity_order = 3 - fem_lu = self.lu - lu_diag = fem_lu.diagonal() - number_of_nodes = len(self.mesh_data.nodes) - size = number_of_nodes*3*self.harmonic_order*2 - - center_diag = np.zeros(size) - for i in range(len(lu_diag)): - diag_value_first = 3/4*lu_diag[i]*(3*u[2*i]**2+u[2*i+1]**2) - diag_value_second = 3/4*lu_diag[i]*(u[2*i]**2+3*u[2*i+1]**2) - - center_diag[2*i] = diag_value_first - center_diag[2*i+1] = diag_value_second - - left_diag = np.zeros(size-1) - left_diag[::2] = np.multiply(u[1::2], lu_diag) - - right_diag = np.zeros(size-1) - right_diag[::2] = np.multiply(u[:-1:2], lu_diag) - - return hb_m + hb_c + hb_k + sparse.diags( - diagonals=[ - left_diag, - center_diag, - right_diag, - ], - offsets=[-1, 0, 1], - format="csc" - ) - - - # -------- Static functions -------- - - @staticmethod - def calculate_tangent_matrix_numerical( - u: npt.NDArray, - nonlinear_force: npt.NDArray, - residual: Callable - ) -> sparse.csc_array: - """Calculates the tanget stiffness matrix numerically using a first - order finite difference approximation. - Important note: Calculation is very slow. - - Parameters: - u: Current solution vector. - nonlinear_force: Vector of nonlinear forces. - residual: Residual function. - - Returns: - Tangential stiffness matrix in sparse csc format. - """ - h = 1e-14 - k_tangent = sparse.lil_matrix( - (len(u), len(u)), - dtype=np.float64 - ) - for i in range(len(u)): - e_m = np.zeros(len(u)) - e_m[i] = h - k_tangent[:, i] = ( - residual(u+e_m, nonlinear_force) - - residual(u, nonlinear_force) - )/h - - return k_tangent.tocsc() - - - @staticmethod - def apply_dirichlet_bc( m: sparse.lil_array, c: sparse.lil_array, k: sparse.lil_array, @@ -471,33 +237,10 @@ def apply_dirichlet_bc( sparse.csc_array, sparse.csc_array ]: - """Applies dirichlet boundary conditions to the given matrix based on - the given nodes. Essentially the rows of the m c and k matrices at the - node indices are set to 0. The element of the k matrix at position - (node_index, node_index) is set to 1. - - Parameters: - m: Mass matrix. - c: Damping matrix. - k: Stiffness matrix. - nodes: Nodes at which the dirichlet boundary conditions shall be - applied. - - Returns: - Modified mass, damping and stiffness matrix. - """ # Set rows of matrices to 0 and diagonal of K to 1 (at node points) - # Matrices for u_r component - for node in nodes: - # Set rows to 0 - if m is not None: - m[node, :] = 0 - if c is not None: - c[node, :] = 0 - if k is not None: - k[node, :] = 0 - - # Set diagonal values to 1 - k[node, node] = 1 + m[nodes, :] = 0 + c[nodes, :] = 0 + k[nodes, :] = 0 + k[nodes, nodes] = 1 return m.tocsc(), c.tocsc(), k.tocsc() diff --git a/scripts/example_hb.py b/scripts/example_hb.py new file mode 100644 index 0000000..23f435c --- /dev/null +++ b/scripts/example_hb.py @@ -0,0 +1,163 @@ +"""Implements an example on how to run a staionary nonlinear simulation.""" + +# Python standard libraries +import os + +# Third party libraries +import numpy as np +import matplotlib.pyplot as plt +from scipy import signal + +# Local libraries +import plutho + + +pic181_small_1 = plutho.MaterialData( + **{ + "c11": 145235418881.3833, + "c12": 92239167225.64952, + "c13": 81099198743.00365, + "c33": 129637003240.59453, + "c44": 22909547556.381878, + "e15": 11.117240926254714, + "e31": -5.043538535682103, + "e33": 13.828571335580783, + "eps11": 1.7215323179892597e-09, + "eps33": 5.5430630756095885e-09, + "alpha_m": 11.382565204240533, + "alpha_k": 4.789248360238781e-10, + "thermal_conductivity": 0, + "heat_capacity": 0, + "density": 7850, + "temperatures": [] + } +) + + +def simulate_hb(CWD, mesh, frequencies): + # Simulation parameters + ZETA = 10 + AMPLITUDE = 1 + HB_ORDER = 3 + + nonlinearity = plutho.Nonlinearity() + nonlinearity.set_cubic_rayleigh(ZETA) + + # Create simulation + sim = plutho.NLPiezoHB( + sim_name, + mesh, + nonlinearity, + HB_ORDER + ) + + # Set materials + sim.add_material( + material_name="pic181", + material_data=pic181_small_1, + physical_group_name="" # Means all elements + ) + + sim.add_dirichlet_bc( + plutho.FieldType.PHI, + "Electrode", + AMPLITUDE*np.ones(len(frequencies)) + ) + sim.add_dirichlet_bc( + plutho.FieldType.PHI, + "Ground", + np.zeros(len(frequencies)) + ) + + # Run simulation + sim.assemble() + sim.simulate(frequencies, tolerance=1e-5) + sim.calculate_charge("Electrode", is_complex=True) + + # Save results + if not os.path.isdir(CWD): + os.makedirs(CWD) + + u_file = os.path.join(CWD, "u.npy") + q_file = os.path.join(CWD, "q.npy") + np.save(u_file, sim.u) + np.save(q_file, sim.q) + + return sim.q + + +def simulate_piezo_freq(mesh, frequencies): + sim = plutho.PiezoFreq( + simulation_name="pincic_test", + mesh=mesh + ) + + sim.add_material( + material_name="pic181", + material_data=pic181, + physical_group_name="" + ) + + sim.add_dirichlet_bc( + field_type=plutho.FieldType.PHI, + physical_group_name="Electrode", + values=np.ones(len(frequencies)) + ) + sim.add_dirichlet_bc( + field_type=plutho.FieldType.PHI, + physical_group_name="Ground", + values=np.zeros(len(frequencies)) + ) + + sim.assemble() + sim.simulate( + frequencies, + False + ) + + sim.calculate_charge("Electrode", is_complex=True) + + return sim.q + + +def plot_impedances(charge_pincic, charge_plutho, frequencies): + impedance_pincic = np.abs(1/(1j*2*np.pi*frequencies*charge_pincic)) + impedance_plutho = np.abs(1/(1j*2*np.pi*frequencies*charge_plutho)) + plt.plot(frequencies/1e6, impedance_pincic, label="Pincic") + plt.plot(frequencies/1e6, impedance_plutho, "--", label="Plutho") + plt.legend() + plt.grid() + plt.show() + +if __name__ == "__main__": + CWD = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "simulations" + ) + + if not os.path.isdir(CWD): + os.makedirs(CWD) + + # Load/create ring mesh + mesh_file = os.path.join(CWD, "ring_mesh.msh") + if not os.path.exists(mesh_file): + plutho.Mesh.generate_rectangular_mesh( + mesh_file, + width=0.00635, + height=0.001, + x_offset=0.0026, + mesh_size=0.0001 + ) + mesh = plutho.Mesh(mesh_file, element_order=1) + + sim_name = "hb_test" + hb_wd = os.path.join(CWD, sim_name) + + frequencies = np.linspace(122e3, 128e3, num=500) + + ## Simulate + if True: + # simulate_nonlinear_stationary(CWD) + q_hb = simulate_hb(hb_wd, mesh, frequencies) + q_piezo = simulate_piezo_freq(mesh, frequencies) + plot_impedances(q_hb, q_piezo, frequencies) From 48771cde208462188d843e0b6ed4bec9bc93f4be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20H=C3=B6lscher?= Date: Fri, 14 Nov 2025 15:26:04 +0100 Subject: [PATCH 05/22] Harmonic balancing change to sin cos ansatz --- plutho/simulations/nonlinear/piezo_hb.py | 156 +++++++++++++++-------- scripts/example_hb.py | 83 +++++------- 2 files changed, 131 insertions(+), 108 deletions(-) diff --git a/plutho/simulations/nonlinear/piezo_hb.py b/plutho/simulations/nonlinear/piezo_hb.py index 33668d1..09d6f24 100644 --- a/plutho/simulations/nonlinear/piezo_hb.py +++ b/plutho/simulations/nonlinear/piezo_hb.py @@ -47,6 +47,7 @@ def __init__( self.nonlinearity = nonlinearity self.nonlinearity.set_mesh_data(self.mesh_data, self.node_points) self.hb_order = hb_order + self.u_hb = [] def assemble(self): """Redirect to general nonlinear assembly function""" @@ -87,12 +88,22 @@ def simulate( # Prepare array u = np.zeros( - (len(frequencies), 3*self.hb_order*number_of_nodes), - dtype=np.complex128 + (len(frequencies), 2*3*self.hb_order*number_of_nodes) ) + # Linear tangent matrix can be created beforehand and scaled with + # frequency later + tan_k, tan_a1, tan_a2 = self.tangent_linear(m, c, k) + print("Starting harmonic balancing simulation") for frequency_index, frequency in enumerate(frequencies): + # Construct whole linear tangent matrix + angular_frequency = 2*np.pi*frequency + tangent_linear = ( + tan_k + angular_frequency * tan_a1 + + angular_frequency**2 * tan_a2 + ).tocsc() + f = self._get_load_vector( dirichlet_nodes, dirichlet_values[:, frequency_index] @@ -105,10 +116,10 @@ def simulate( u_i = u[frequency_index, :] # Check if initial value is already sufficient - best_norm = scipy.linalg.norm(self.residual( - m, c, k, u_i, f, frequency - )) + residual = self.residual(tangent_linear, u_i, f, frequency) + best_norm = np.linalg.norm(residual) if best_norm < tolerance: + print("Initial value already sufficient") u[frequency_index, :] = u_i continue best_u_i = u_i @@ -117,23 +128,30 @@ def simulate( converged = False for i in range(max_iter): # Calculate next guess for u using tangent matrix - tangent_matrix = self.tangent_matrix( - u_i, - m, - c, - k, - frequency - ) - delta_u = slin.spsolve( - tangent_matrix.tocsc(), - self.residual(m, c, k, u_i, f, frequency) - ) + # tangent_matrix = self.tangent_matrix_nl( + # u_i, + # m, + # c, + # k, + # frequency + # ) + tangent_linear + tangent_matrix = tangent_linear + + # delta_u = slin.spsolve( + # tangent_matrix, + # self.residual(tangent_linear, u_i, f, frequency) + # ) + lu = slin.splu(tangent_matrix) + delta_u = lu.solve(residual) u_i_next = u_i - delta_u + # Update residual + residual = self.residual( + tangent_linear, u_i_next, f, frequency + ) + # Check for convergence - norm = scipy.linalg.norm(self.residual( - m, c, k, u_i_next, f, frequency - )) + norm = np.linalg.norm(residual) if norm < tolerance: # Newton converged u[frequency_index, :] = u_i_next @@ -154,51 +172,80 @@ def simulate( f"{frequency_index}. Choosing best value: {best_norm}") u[frequency_index, :] = best_u_i - self.u = u + for n in range(self.hb_order): + idx = 2*n + self.u_hb.append( + u[:, idx*3*number_of_nodes:(idx+1)*3*number_of_nodes] + + 1j*u[:, (idx+1)*3*number_of_nodes:(idx+2)*3*number_of_nodes] + ) - def residual(self, m, c, k, u, f, frequency): - number_of_nodes = len(self.mesh_data.nodes) - res = np.zeros(self.hb_order*3*number_of_nodes, dtype=np.complex128) - angular_frequency = 2*np.pi*frequency + self.u = self.u_hb[0] + + def residual(self, res_matrix, u, f): + return res_matrix.dot(u)-f + + def tangent_linear(self, m, c, k): + k_blocks = [ + [None for _ in range(2*self.hb_order)] + for _ in range(2*self.hb_order) + ] + + a1_blocks = [ + [None for _ in range(2*self.hb_order)] + for _ in range(2*self.hb_order) + ] + + a2_blocks = [ + [None for _ in range(2*self.hb_order)] + for _ in range(2*self.hb_order) + ] for n in range(self.hb_order): - u_n = u[n*3*number_of_nodes:(n+1)*3*number_of_nodes] - f_n = f[n*3*number_of_nodes:(n+1)*3*number_of_nodes] - res[n*3*number_of_nodes:(n+1)*3*number_of_nodes] = ( - - angular_frequency**2*(n+1)**2*m@u_n - + 1j*angular_frequency*(n+1)*c@u_n - + k@u_n - - f_n - ) + i = 2*n + j = 2*n + 1 + np1 = n + 1 - # Add nonlinearity - if self.hb_order >= 3: - u_3 = u[2*3*number_of_nodes:3*3*number_of_nodes] - res[2*3*number_of_nodes:3*3*number_of_nodes] += \ - self.nonlinearity.evaluate_force_vector(u_3, m, c, k) + # constant part (k) + k_blocks[i][i] = k + k_blocks[j][j] = k - return res + # ω part (c) + a1_blocks[i][j] = np1 * c + a1_blocks[j][i] = -np1 * c - def tangent_matrix(self, u, m, c, k, frequency): - angular_frequency = 2*np.pi*frequency + # ω² part (m) + a2_blocks[i][i] = -(np1**2) * m + a2_blocks[j][j] = -(np1**2) * m + + # Convert to sparse once + k = sparse.block_array(k_blocks, format="csc") + a1 = sparse.block_array(a1_blocks, format="csc") + a2 = sparse.block_array(a2_blocks, format="csc") + + return k, a1, a2 + + def tangent_nonlinear(self, u, m, c, k, frequency): number_of_nodes = len(self.mesh_data.nodes) + angular_frequency = 2*np.pi*frequency - blocks = [] + blocks = [ + [None for _ in range(2*self.hb_order)] + for _ in range(2*self.hb_order) + ] + # This can be done beforehand and not every iteration for n in range(self.hb_order): - u_n = u[n*3*number_of_nodes:(n+1)*3*number_of_nodes] - blocks.append( - - angular_frequency**2*(n+1)**2*m - + 1j*angular_frequency*(n+1)*c - + k - + self.nonlinearity.evaluate_jacobian(u_n, m, c, k) - ) + # cos eq derived after cos part + blocks[2*n][2*n] = -(n+1)**2*angular_frequency**2*m+k + # cos eq derived after sin part + blocks[2*n][2*n+1] = (n+1)*angular_frequency*c + # sin eq derived after cos part + blocks[2*n+1][2*n] = -(n+1)*angular_frequency*c + # sin eq derived after sin part + blocks[2*n+1][2*n+1] = -(n+1)**2*angular_frequency**2*m+k - # Add nonlinearity - if self.hb_order >= 3: - u_3 = u[2*3*number_of_nodes:3*3*number_of_nodes] - blocks[2] += self.nonlinearity.evaluate_jacobian(u_3, m, c, k) + # TODO Add nonlinarity - return sparse.block_diag(blocks) + return sparse.block_array(blocks) def _get_load_vector( self, @@ -219,8 +266,9 @@ def _get_load_vector( # Can be initialized to 0 because external load and volume # charge density is 0. - f = np.zeros(self.hb_order*3*number_of_nodes, dtype=np.float64) + f = np.zeros(2*self.hb_order*3*number_of_nodes, dtype=np.float64) + # TODO Right now only the base frequency cosine part is set # Set dirichlet bc f[nodes] = values diff --git a/scripts/example_hb.py b/scripts/example_hb.py index 23f435c..d4f897b 100644 --- a/scripts/example_hb.py +++ b/scripts/example_hb.py @@ -34,18 +34,16 @@ ) -def simulate_hb(CWD, mesh, frequencies): +def simulate_hb(CWD, mesh, frequencies, zeta, amplitude): # Simulation parameters - ZETA = 10 - AMPLITUDE = 1 - HB_ORDER = 3 + HB_ORDER = 1 nonlinearity = plutho.Nonlinearity() - nonlinearity.set_cubic_rayleigh(ZETA) + nonlinearity.set_cubic_rayleigh(zeta) # Create simulation sim = plutho.NLPiezoHB( - sim_name, + "HB_Test", mesh, nonlinearity, HB_ORDER @@ -61,7 +59,7 @@ def simulate_hb(CWD, mesh, frequencies): sim.add_dirichlet_bc( plutho.FieldType.PHI, "Electrode", - AMPLITUDE*np.ones(len(frequencies)) + amplitude*np.ones(len(frequencies)) ) sim.add_dirichlet_bc( plutho.FieldType.PHI, @@ -71,7 +69,7 @@ def simulate_hb(CWD, mesh, frequencies): # Run simulation sim.assemble() - sim.simulate(frequencies, tolerance=1e-5) + sim.simulate(frequencies) sim.calculate_charge("Electrode", is_complex=True) # Save results @@ -86,49 +84,18 @@ def simulate_hb(CWD, mesh, frequencies): return sim.q -def simulate_piezo_freq(mesh, frequencies): - sim = plutho.PiezoFreq( - simulation_name="pincic_test", - mesh=mesh - ) - - sim.add_material( - material_name="pic181", - material_data=pic181, - physical_group_name="" - ) - - sim.add_dirichlet_bc( - field_type=plutho.FieldType.PHI, - physical_group_name="Electrode", - values=np.ones(len(frequencies)) - ) - sim.add_dirichlet_bc( - field_type=plutho.FieldType.PHI, - physical_group_name="Ground", - values=np.zeros(len(frequencies)) - ) - - sim.assemble() - sim.simulate( - frequencies, - False - ) - - sim.calculate_charge("Electrode", is_complex=True) - - return sim.q +def plot_impedances(charges, amplitudes, frequencies): + for charge, amplitude in zip(charges, amplitudes): + impedance = np.abs(amplitude/(1j*2*np.pi*frequencies*charge)) + plt.plot(frequencies/1e6, impedance, label=f"{amplitude} V") - -def plot_impedances(charge_pincic, charge_plutho, frequencies): - impedance_pincic = np.abs(1/(1j*2*np.pi*frequencies*charge_pincic)) - impedance_plutho = np.abs(1/(1j*2*np.pi*frequencies*charge_plutho)) - plt.plot(frequencies/1e6, impedance_pincic, label="Pincic") - plt.plot(frequencies/1e6, impedance_plutho, "--", label="Plutho") + plt.xlabel("Frequency / kHz") + plt.ylabel("Impedance / $\\Omega$") plt.legend() plt.grid() plt.show() + if __name__ == "__main__": CWD = os.path.join( os.path.dirname(os.path.abspath(__file__)), @@ -150,14 +117,22 @@ def plot_impedances(charge_pincic, charge_plutho, frequencies): ) mesh = plutho.Mesh(mesh_file, element_order=1) - sim_name = "hb_test" - hb_wd = os.path.join(CWD, sim_name) - - frequencies = np.linspace(122e3, 128e3, num=500) + frequencies = np.linspace(100e3, 120e3, num=500) ## Simulate + # simulate_nonlinear_stationary(CWD) + ZETA = 00 + amplitudes = [0.7, 3.6, 6.6] + if True: + charges = [] + for amplitude in amplitudes[:1]: + hb_wd = os.path.join(CWD, f"hb_test_{amplitude}") + charges.append(simulate_hb(hb_wd, mesh, frequencies, ZETA, amplitude)) + if True: - # simulate_nonlinear_stationary(CWD) - q_hb = simulate_hb(hb_wd, mesh, frequencies) - q_piezo = simulate_piezo_freq(mesh, frequencies) - plot_impedances(q_hb, q_piezo, frequencies) + charges = [] + for amplitude in amplitudes[:1]: + hb_wd = os.path.join(CWD, f"hb_test_{amplitude}") + charge = np.load(os.path.join(hb_wd, "q.npy")) + charges.append(charge) + plot_impedances(charges, amplitudes, frequencies) From 11bfbcc76f34fba6c22aef1cac9436b4edbac350 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20H=C3=B6lscher?= Date: Wed, 19 Nov 2025 11:14:32 +0100 Subject: [PATCH 06/22] Fix bug in nonlinear matrix calculation --- plutho/simulations/nonlinear/base.py | 15 ++++++++------- plutho/simulations/nonlinear/piezo_time.py | 11 +++++------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/plutho/simulations/nonlinear/base.py b/plutho/simulations/nonlinear/base.py index 81e7044..38e51dc 100644 --- a/plutho/simulations/nonlinear/base.py +++ b/plutho/simulations/nonlinear/base.py @@ -305,9 +305,6 @@ def set_cubic_custom(self, nonlinear_data: npt.NDArray): def evaluate_force_vector( self, u: npt.NDArray, - m: sparse.csc_array, - c: sparse.csc_array, - k: sparse.csc_array ) -> npt.NDArray: """Evaluates the nonlinear force vector based on the previously set nonlinearity and the given displacement field u and the FEM matrices. @@ -350,9 +347,6 @@ def evaluate_force_vector( def evaluate_jacobian( self, u: npt.NDArray, - m: sparse.csc_array, - c: sparse.csc_array, - k: sparse.csc_array ): """Evaluates the jacobian of the nonlinear force vector based on the current displacement u and the FEM matrices. @@ -423,6 +417,9 @@ def assemble( c: FEM damping matrix. k: FEM stiffness matrix. """ + nodes = self.mesh_data.nodes + number_of_nodes = len(nodes) + if self.nonlinear_type is None: raise ValueError("Cannot assemble matrices, since no \ nonlinear type is set") @@ -430,7 +427,11 @@ def assemble( match self.nonlinear_type: case NonlinearType.QuadraticRayleigh | \ NonlinearType.CubicRayleigh: - self.ln = k*self.zeta + self.ln = sparse.lil_array( + (3*number_of_nodes, 3*number_of_nodes) + ) + self.ln[:2*number_of_nodes, :2*number_of_nodes] = \ + k[:2*number_of_nodes, :2*number_of_nodes]*self.zeta case NonlinearType.QuadraticCustom: self._assemble_quadratic_custom() case NonlinearType.CubicCustom: diff --git a/plutho/simulations/nonlinear/piezo_time.py b/plutho/simulations/nonlinear/piezo_time.py index fd3b8b4..756157c 100644 --- a/plutho/simulations/nonlinear/piezo_time.py +++ b/plutho/simulations/nonlinear/piezo_time.py @@ -44,7 +44,7 @@ def __init__( self.nonlinearity.set_mesh_data(self.mesh_data, self.node_points) def assemble(self): - """Redirect to general nonlinear assembly function""" + """Assembles the matrices based on the set material and mesh.""" self.material_manager.initialize_materials() m, c, k = assemble( @@ -191,11 +191,10 @@ def residual(next_u, current_u, v, a, f): )) if norm < tolerance: - # print( - # f"Newton converged at time step {time_index} " - # f"after {i+1} iteration(s)" - # ) - # print(u_i_next) + print( + f"Newton converged at time step {time_index} " + f"after {i+1} iteration(s)" + ) next_u = u_i_next self.converged = True break From ee15df5d040a425a11d144462e90cbd22404a811 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20H=C3=B6lscher?= Date: Wed, 19 Nov 2025 11:15:14 +0100 Subject: [PATCH 07/22] Add working nl hbfem simulation --- plutho/simulations/nonlinear/piezo_hb.py | 420 +++++++++++++++--- scripts/hb/compare_impedance.py | 156 +++++++ .../{example_hb.py => hb/run_nonlinear.py} | 57 ++- scripts/test_jacobian.py | 77 ++++ 4 files changed, 628 insertions(+), 82 deletions(-) create mode 100644 scripts/hb/compare_impedance.py rename scripts/{example_hb.py => hb/run_nonlinear.py} (74%) create mode 100644 scripts/test_jacobian.py diff --git a/plutho/simulations/nonlinear/piezo_hb.py b/plutho/simulations/nonlinear/piezo_hb.py index 09d6f24..55e40f6 100644 --- a/plutho/simulations/nonlinear/piezo_hb.py +++ b/plutho/simulations/nonlinear/piezo_hb.py @@ -9,7 +9,6 @@ import numpy.typing as npt from scipy import sparse import scipy.sparse.linalg as slin -import scipy # Local libraries from ...enums import SolverType @@ -50,7 +49,7 @@ def __init__( self.u_hb = [] def assemble(self): - """Redirect to general nonlinear assembly function""" + """Assembles the matrices based on the set material and mesh.""" self.material_manager.initialize_materials() m, c, k = assemble( @@ -63,12 +62,61 @@ def assemble(self): self.nonlinearity.assemble(m, c, k) + def simulate_linear( + self, + frequency: float, + dirichlet_nodes: npt.NDArray, + dirichlet_values: npt.NDArray + ) -> npt.NDArray: + """Runs a linear simulation for one frequency and returns the whole + u vector. + + Parameters: + frequency: Frequency at which the simulation is done. + + Returns: + Harmonic balancing u vector. + """ + m = self.m.copy() + c = self.c.copy() + k = self.k.copy() + + # Apply dirichlet bc to matrices + m, c, k = self._apply_dirichlet_bc( + m, + c, + k, + dirichlet_nodes + ) + + # Prepare arrays + f = self._get_load_vector( + dirichlet_nodes, + dirichlet_values + ) + + # Calculate system matrix + angular_frequency = 2*np.pi*frequency + m, c, k = self.get_linear_system_matrix(m, c, k) + s = m*angular_frequency**2+c*angular_frequency+k + + # Solve system + lu = slin.splu(s) + return lu.solve(f) + def simulate( self, frequencies: npt.NDArray, tolerance: float = 1e-11, - max_iter: int = 300, + max_iter: int = 300 ): + """Runs the nonlinear simulation at the given frequencies. + + Parameters: + frequencies: Frequencies for which the simulation is done. + tolerance: Tolerance for the residual norm. + max_iter: Maximum number of iterations for each frequency. + """ dirichlet_nodes = np.array(self.dirichlet_nodes) dirichlet_values = np.array(self.dirichlet_values) number_of_nodes = len(self.mesh_data.nodes) @@ -86,22 +134,29 @@ def simulate( ) self.nonlinearity.apply_dirichlet_bc(dirichlet_nodes) - # Prepare array + # Prepare array and guess initial value using linear simulation u = np.zeros( (len(frequencies), 2*3*self.hb_order*number_of_nodes) ) + u[0, :] = self.simulate_linear( + frequencies[0], + dirichlet_nodes, + dirichlet_values[:, 0] + ) # Linear tangent matrix can be created beforehand and scaled with # frequency later - tan_k, tan_a1, tan_a2 = self.tangent_linear(m, c, k) + # Since its linear it is equal to the tangent matrix for the linear + # part + m_tan, c_tan, k_tan = self.get_linear_system_matrix(m, c, k) print("Starting harmonic balancing simulation") for frequency_index, frequency in enumerate(frequencies): - # Construct whole linear tangent matrix + # Construct whole linear tangent matrix using the current frequency angular_frequency = 2*np.pi*frequency tangent_linear = ( - tan_k + angular_frequency * tan_a1 + - angular_frequency**2 * tan_a2 + k_tan + angular_frequency * c_tan + + angular_frequency**2 * m_tan ).tocsc() f = self._get_load_vector( @@ -116,7 +171,9 @@ def simulate( u_i = u[frequency_index, :] # Check if initial value is already sufficient - residual = self.residual(tangent_linear, u_i, f, frequency) + residual = self.residual( + tangent_linear, u_i, f, frequency + ) best_norm = np.linalg.norm(residual) if best_norm < tolerance: print("Initial value already sufficient") @@ -128,19 +185,11 @@ def simulate( converged = False for i in range(max_iter): # Calculate next guess for u using tangent matrix - # tangent_matrix = self.tangent_matrix_nl( - # u_i, - # m, - # c, - # k, - # frequency - # ) + tangent_linear - tangent_matrix = tangent_linear - - # delta_u = slin.spsolve( - # tangent_matrix, - # self.residual(tangent_linear, u_i, f, frequency) - # ) + tangent_matrix = tangent_linear + self.tangent_nonlinear( + u_i, frequency + ) + + # TODO Faster than slin.spsolve? -> Change in other solvers? lu = slin.splu(tangent_matrix) delta_u = lu.solve(residual) u_i_next = u_i - delta_u @@ -172,30 +221,55 @@ def simulate( f"{frequency_index}. Choosing best value: {best_norm}") u[frequency_index, :] = best_u_i - for n in range(self.hb_order): - idx = 2*n - self.u_hb.append( - u[:, idx*3*number_of_nodes:(idx+1)*3*number_of_nodes] - + 1j*u[:, (idx+1)*3*number_of_nodes:(idx+2)*3*number_of_nodes] - ) + self.u = u + + def residual( + self, + linear_matrix: sparse.csc_array, + u: npt.NDArray, + f: npt.NDArray, + frequency: float + ) -> npt.NDArray: + """Calculates the residual for the nonlinear simulation. - self.u = self.u_hb[0] + Parameters: + linear_matrix:""" + return linear_matrix.dot(u)-f+self.residual_nonlinear( + u, frequency + ) - def residual(self, res_matrix, u, f): - return res_matrix.dot(u)-f + def get_linear_system_matrix( + self, + m_fem: sparse.csc_array, + c_fem: sparse.csc_array, + k_fem: sparse.csc_array + ) -> Tuple[sparse.csc_array, sparse.csc_array, sparse.csc_array]: + """Calculates the matrices for the linear system. Since they have to + incoroporate the simulation frequency, a tuple of 3 matrices is + returned. To obtain the whole system matrix it is necessary to combine + them using the angular frequency: + angular_frequency**2*m+angular_frequency*c+k - def tangent_linear(self, m, c, k): + Parameters: + m: FEM mass matrix. + c: FEM damping matrix. + k: FEM stiffness matrix. + + Returns: + A tuple of the HB mass matrix, HB damping matrix and HB stiffness + matrix. + """ k_blocks = [ [None for _ in range(2*self.hb_order)] for _ in range(2*self.hb_order) ] - a1_blocks = [ + c_blocks = [ [None for _ in range(2*self.hb_order)] for _ in range(2*self.hb_order) ] - a2_blocks = [ + m_blocks = [ [None for _ in range(2*self.hb_order)] for _ in range(2*self.hb_order) ] @@ -206,46 +280,201 @@ def tangent_linear(self, m, c, k): np1 = n + 1 # constant part (k) - k_blocks[i][i] = k - k_blocks[j][j] = k + k_blocks[i][i] = k_fem + k_blocks[j][j] = k_fem # ω part (c) - a1_blocks[i][j] = np1 * c - a1_blocks[j][i] = -np1 * c + c_blocks[i][j] = np1 * c_fem + c_blocks[j][i] = -np1 * c_fem # ω² part (m) - a2_blocks[i][i] = -(np1**2) * m - a2_blocks[j][j] = -(np1**2) * m + m_blocks[i][i] = -(np1**2) * m_fem + m_blocks[j][j] = -(np1**2) * m_fem # Convert to sparse once - k = sparse.block_array(k_blocks, format="csc") - a1 = sparse.block_array(a1_blocks, format="csc") - a2 = sparse.block_array(a2_blocks, format="csc") + k_hb = sparse.block_array(k_blocks, format="csc") + c_hb = sparse.block_array(c_blocks, format="csc") + m_hb = sparse.block_array(m_blocks, format="csc") + + return m_hb, c_hb, k_hb + + def _time_to_freq( + self, + u_time: npt.NDArray, + frequency: float + ) -> npt.NDArray: + """Converts the a vector with dimensions [dof, time] to the HB u + vector. + + Parameters: + u_time: Vector of u in time domain. + frequency: Base frequency. + + Returns: + The HB u vector in frequency domain. + """ + N = 2*self.hb_order+1 + angular_frequency = 2*np.pi*frequency + t = np.linspace(0, 1/frequency, num=N, endpoint=False) + + # Base functions + harmonics = np.arange(1, self.hb_order+1)[:, np.newaxis] + cos_basis = np.cos(harmonics * angular_frequency * t) + sin_basis = np.sin(harmonics * angular_frequency * t) + + # Convert to freq domain + u_cos = (2/N) * (u_time @ cos_basis.T) + u_sin = (2/N) * (u_time @ sin_basis.T) + + # Reorder for simulation + u_freq = np.empty(2*self.hb_order*u_time.shape[0]) + for n in range(self.hb_order): + dof = u_time.shape[0] + u_freq[2*n*dof:(2*n+1)*dof] = u_cos[:, n] + u_freq[(2*n+1)*dof:(2*n+2)*dof] = u_sin[:, n] - return k, a1, a2 + return u_freq - def tangent_nonlinear(self, u, m, c, k, frequency): + def _freq_to_time( + self, + u_freq: npt.NDArray, + frequency: float + ) -> npt.NDArray: + """Converts the HB u vector in frequency domain in a time domain + vector. + + Parameters: + u_freq: HB u vector in frequency domain. + frequency: Base frequency. + + Returns: + HB u vector in time domain. + """ number_of_nodes = len(self.mesh_data.nodes) + dof = 3*number_of_nodes + N = 2*self.hb_order+1 angular_frequency = 2*np.pi*frequency + t = np.linspace(0, 1/frequency, num=N, endpoint=False) - blocks = [ - [None for _ in range(2*self.hb_order)] - for _ in range(2*self.hb_order) - ] - # This can be done beforehand and not every iteration + # Base functions + harmonics = np.arange(1, self.hb_order+1)[:, np.newaxis] + cos_basis = np.cos(harmonics * angular_frequency * t) + sin_basis = np.sin(harmonics * angular_frequency * t) + + u_time = np.zeros((dof, N)) for n in range(self.hb_order): - # cos eq derived after cos part - blocks[2*n][2*n] = -(n+1)**2*angular_frequency**2*m+k - # cos eq derived after sin part - blocks[2*n][2*n+1] = (n+1)*angular_frequency*c - # sin eq derived after cos part - blocks[2*n+1][2*n] = -(n+1)*angular_frequency*c - # sin eq derived after sin part - blocks[2*n+1][2*n+1] = -(n+1)**2*angular_frequency**2*m+k + u_time += np.outer( + u_freq[2*n*dof:(2*n+1)*dof], + cos_basis[n, :] + ) + u_time += np.outer( + u_freq[(2*n+1)*dof:(2*n+2)*dof], + sin_basis[n, :] + ) + + return u_time + + def residual_nonlinear( + self, + u: npt.NDArray, + frequency: float + ) -> npt.NDArray: + """Calculates the nonlinear residual of the given HB u vector. + + Parameters: + u: HB u vector. + frequency: Base frequency. + + Returns: + Nonlinear residual vector. + """ + u_time = self._freq_to_time(u, frequency) + + # Apply nonlinearity in time domain + f_nl_time = np.zeros(shape=u_time.shape) + for i in range(u_time.shape[1]): + f_nl_time[:, i] = self.nonlinearity.evaluate_force_vector( + u_time[:, i] + ) + + return self._time_to_freq(f_nl_time, frequency) + + def tangent_nonlinear( + self, + u: npt.NDArray, + frequency: float + ) -> sparse.csc_array: + """Calculates the tangent matrix for the nonlinear part. + + Parameters: + u: Current HB u vector. + frequency: Base frequency. - # TODO Add nonlinarity + Returns: + Tangent stiffness matrix (jacobian) of the nonlinear part. + """ + number_of_nodes = len(self.mesh_data.nodes) + dof = 3*number_of_nodes + angular_frequency = 2*np.pi*frequency + N = 2*self.hb_order+1 + t = np.linspace(0, 1/frequency, num=N, endpoint=False) + + # Base functions + harmonics = np.arange(1, self.hb_order+1)[:, np.newaxis] + cos_basis = np.cos(harmonics * angular_frequency * t) + sin_basis = np.sin(harmonics * angular_frequency * t) - return sparse.block_array(blocks) + # Calculate idft + u_time = np.zeros((dof, N)) + for n in range(self.hb_order): + u_time += np.outer(u[2*n*dof:(2*n+1)*dof], cos_basis[n, :]) + u_time += np.outer(u[(2*n+1)*dof:(2*n+2)*dof], sin_basis[n, :]) + + # Build jacobian from sub-blocks and calculate dft of df_du_time + df_du_time = u_time ** 2 + blocks = [] + for i in range(self.hb_order): + # Cos part + row_blocks = [] + for j in range(self.hb_order): + # Cos cos part + integrand = df_du_time * cos_basis[i, :] * cos_basis[j, :] + cc_diag = (2/N) * np.sum(integrand, axis=1) + cc_diag_sparse = sparse.diags_array([cc_diag], offsets=[0]) + cc = 3*self.nonlinearity.ln@cc_diag_sparse + + # Cos sin part + integrand = df_du_time * cos_basis[i, :] * sin_basis[j, :] + cs_diag = (2/N) * np.sum(integrand, axis=1) + cs_diag_sparse = sparse.diags_array([cs_diag], offsets=[0]) + cs = 3*self.nonlinearity.ln@cs_diag_sparse + + row_blocks.append(cc) + row_blocks.append(cs) + + blocks.append(row_blocks) + + # Sin part + row_blocks = [] + for j in range(self.hb_order): + # Sin cos part + integrand = df_du_time * sin_basis[i, :] * cos_basis[j, :] + sc_diag = (2/N) * np.sum(integrand, axis=1) + sc_diag_sparse = sparse.diags_array([sc_diag], offsets=[0]) + sc = 3*self.nonlinearity.ln@sc_diag_sparse + + # Sin sin part + integrand = df_du_time * sin_basis[i, :] * sin_basis[j, :] + ss_diag = (2/N) * np.sum(integrand, axis=1) + ss_diag_sparse = sparse.diags_array([ss_diag], offsets=[0]) + ss = 3*self.nonlinearity.ln@ss_diag_sparse + + row_blocks.append(sc) + row_blocks.append(ss) + + blocks.append(row_blocks) + + return sparse.bmat(blocks, format='csc') def _get_load_vector( self, @@ -292,3 +521,74 @@ def _apply_dirichlet_bc( k[nodes, nodes] = 1 return m.tocsc(), c.tocsc(), k.tocsc() + + def test_jacobian(self, epsilon: float = 1e-6) -> Tuple[float, float]: + """Tests the analytical jacobian matrix with a jacobian calculated from + a finite difference approximation using the given u and epsilon. + + Parameters: + u: Example u vector. + + Returns: + Tuple of absolute and relative errors. + """ + # Create test data + nodes, _ = self.mesh.get_mesh_nodes_and_elements() + dof = 3*len(nodes) + frequency = np.random.randn(1)[0] * 1e6 + angular_frequency = 2*np.pi*frequency + u = np.random.randn(2*self.hb_order*dof) * 0.01 + f = np.random.randn(2*self.hb_order*dof) * 0.01 + m = sparse.random(dof, dof, density=0.3, format='csc') + c = sparse.random(dof, dof, density=0.3, format='csc') + k = sparse.random(dof, dof, density=0.3, format='csc') + + # Analytical jacobian + hb_m, hb_c, hb_k = self.get_linear_system_matrix(m, c, k) + hb_s = angular_frequency**2*hb_m+angular_frequency*hb_c+hb_k + J_analytical = hb_s + self.tangent_nonlinear(u, frequency) + + # Finite difference jacobian + n = len(u) + J_fd = np.zeros((n, n)) + + for i in range(n): + u_perturbed = u.copy() + u_perturbed[i] += epsilon + Fp = self.residual(hb_s, u_perturbed, f, frequency) + + u_perturbed[i] -= epsilon + Fm = self.residual(hb_s, u_perturbed, f, frequency) + + J_fd[:, i] = (Fp - Fm) / (2*epsilon) + + # Vergleiche + diff = np.abs(J_analytical.toarray() - J_fd) + max_diff = np.max(diff) + rel_diff = max_diff / (np.max(np.abs(J_fd)) + 1e-10) + + print(f"Max absolute difference: {max_diff}") + print(f"Max relative difference: {rel_diff}") + + return max_diff, rel_diff + + def calculate_charge(self, electrode_name: str, is_complex: bool = True): + """Calculate the charge of hb method. Resulting charge is saved in + self.q. + + Parameters: + electrode_name: Name of the electrode phyiscal group. + is_complex: + """ + nodes, _ = self.mesh.get_mesh_nodes_and_elements() + dof = 3*len(nodes) + hb_u = np.copy(self.u) + q = np.zeros(shape=(self.hb_order, hb_u.shape[0]), dtype=np.complex128) + for n in range(self.hb_order): + self.u = hb_u[:, 2*n*dof:(2*n+1)*dof] \ + + 1j*hb_u[:, (2*n+1)*dof:(2*n+2)*dof] + super().calculate_charge(electrode_name, is_complex=True) + q[n, :] = self.q + + self.u = hb_u + self.q = q diff --git a/scripts/hb/compare_impedance.py b/scripts/hb/compare_impedance.py new file mode 100644 index 0000000..7fa968a --- /dev/null +++ b/scripts/hb/compare_impedance.py @@ -0,0 +1,156 @@ + +# Python standard libraries +import os +import time + +# Third party libraries +import numpy as np +import matplotlib.pyplot as plt + +# Local libraries +import plutho + + +pic181_small_1 = plutho.MaterialData( + **{ + "c11": 145235418881.3833, + "c12": 92239167225.64952, + "c13": 81099198743.00365, + "c33": 129637003240.59453, + "c44": 22909547556.381878, + "e15": 11.117240926254714, + "e31": -5.043538535682103, + "e33": 13.828571335580783, + "eps11": 1.7215323179892597e-09, + "eps33": 5.5430630756095885e-09, + "alpha_m": 11.382565204240533, + "alpha_k": 4.789248360238781e-10, + "thermal_conductivity": 0, + "heat_capacity": 0, + "density": 7850, + "temperatures": [] + } +) + + +def simulate_hb(mesh, frequencies, hb_order): + nonlinearity = plutho.Nonlinearity() + nonlinearity.set_cubic_rayleigh(0) + + # Create simulation + sim = plutho.NLPiezoHB( + "HB_Test_Impedance", + mesh, + nonlinearity, + hb_order + ) + + # Set materials + sim.add_material( + material_name="pic181", + material_data=pic181_small_1, + physical_group_name="" + ) + + sim.add_dirichlet_bc( + plutho.FieldType.PHI, + "Electrode", + np.ones(len(frequencies)) + ) + sim.add_dirichlet_bc( + plutho.FieldType.PHI, + "Ground", + np.zeros(len(frequencies)) + ) + + # Run simulation + sim.assemble() + sim.simulate(frequencies) + sim.calculate_charge("Electrode", is_complex=True) + + return sim.q + + +def simulate_freq(mesh, frequencies): + sim = plutho.PiezoFreq( + simulation_name="pincic_test", + mesh=mesh + ) + + sim.add_material( + material_name="pic181", + material_data=pic181_small_1, + physical_group_name="" + ) + + sim.add_dirichlet_bc( + field_type=plutho.FieldType.PHI, + physical_group_name="Electrode", + values=np.ones(len(frequencies)) + ) + sim.add_dirichlet_bc( + field_type=plutho.FieldType.PHI, + physical_group_name="Ground", + values=np.zeros(len(frequencies)) + ) + + sim.assemble() + sim.simulate( + frequencies, + False + ) + + sim.calculate_charge("Electrode", is_complex=True) + + return sim.q + + +def plot_impedances(charge_freq, charge_hb, frequencies): + impedance_freq = np.abs(1/(1j*2*np.pi*frequencies*charge_freq)) + impedance_hb = np.abs(1/(1j*2*np.pi*frequencies*charge_hb[0, :])) + plt.plot(frequencies/1e6, impedance_freq, label="plutho freq") + plt.plot(frequencies/1e6, impedance_hb, "--", label="plutho hb") + plt.legend() + plt.grid() + plt.show() + + +if __name__ == "__main__": + CWD = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "..", "..", + "simulations" + ) + + if not os.path.isdir(CWD): + os.makedirs(CWD) + + # Load/create ring mesh + mesh_file = os.path.join(CWD, "ring_mesh.msh") + if not os.path.exists(mesh_file): + plutho.Mesh.generate_rectangular_mesh( + mesh_file, + width=0.00635, + height=0.001, + x_offset=0.0026, + mesh_size=0.0001 + ) + mesh = plutho.Mesh(mesh_file, element_order=1) + + # Simulation parameters + frequencies = np.linspace(0, 1e7, 1000)[1:] + HB_ORDER = 3 + + # Simulate impedance + start_time = time.time() + charge_hb = simulate_hb(mesh, frequencies, HB_ORDER) + plutho_hb_time = time.time() - start_time + + start_time = time.time() + charge_freq = simulate_freq(mesh, frequencies) + plutho_freq_time = time.time() - start_time + + print(f"plutho hb took {plutho_hb_time}s to simulate.") + print(f"plutho freq pincic took {plutho_freq_time}s to simulate.") + + plot_impedances(charge_freq, charge_hb, frequencies) diff --git a/scripts/example_hb.py b/scripts/hb/run_nonlinear.py similarity index 74% rename from scripts/example_hb.py rename to scripts/hb/run_nonlinear.py index d4f897b..3fb16bf 100644 --- a/scripts/example_hb.py +++ b/scripts/hb/run_nonlinear.py @@ -1,4 +1,6 @@ -"""Implements an example on how to run a staionary nonlinear simulation.""" +"""Implements an example script for the finite element method harmonic +balacing simulation. +""" # Python standard libraries import os @@ -6,7 +8,7 @@ # Third party libraries import numpy as np import matplotlib.pyplot as plt -from scipy import signal +from multiprocessing import Pool # Local libraries import plutho @@ -34,10 +36,7 @@ ) -def simulate_hb(CWD, mesh, frequencies, zeta, amplitude): - # Simulation parameters - HB_ORDER = 1 - +def simulate_hb(CWD, mesh, frequencies, amplitude, zeta, hb_order): nonlinearity = plutho.Nonlinearity() nonlinearity.set_cubic_rayleigh(zeta) @@ -46,7 +45,7 @@ def simulate_hb(CWD, mesh, frequencies, zeta, amplitude): "HB_Test", mesh, nonlinearity, - HB_ORDER + hb_order ) # Set materials @@ -79,9 +78,8 @@ def simulate_hb(CWD, mesh, frequencies, zeta, amplitude): u_file = os.path.join(CWD, "u.npy") q_file = os.path.join(CWD, "q.npy") np.save(u_file, sim.u) - np.save(q_file, sim.q) - - return sim.q + if sim.q is not None: + np.save(q_file, sim.q) def plot_impedances(charges, amplitudes, frequencies): @@ -99,6 +97,7 @@ def plot_impedances(charges, amplitudes, frequencies): if __name__ == "__main__": CWD = os.path.join( os.path.dirname(os.path.abspath(__file__)), + "..", "..", "simulations" ) @@ -117,22 +116,36 @@ def plot_impedances(charges, amplitudes, frequencies): ) mesh = plutho.Mesh(mesh_file, element_order=1) - frequencies = np.linspace(100e3, 120e3, num=500) - - ## Simulate - # simulate_nonlinear_stationary(CWD) - ZETA = 00 + # Simulation parameters + HB_ORDER = 3 + frequencies = np.linspace(90e3, 120e3, num=1000) + zetas = [0, 1, 10, 50, 100, 500, 1000, 5000] amplitudes = [0.7, 3.6, 6.6] - if True: - charges = [] - for amplitude in amplitudes[:1]: - hb_wd = os.path.join(CWD, f"hb_test_{amplitude}") - charges.append(simulate_hb(hb_wd, mesh, frequencies, ZETA, amplitude)) + # Simulate if True: + args = [] + for zeta in zetas: + for amplitude in amplitudes: + args.append(( + f"hb_test_{amplitude}_{zeta}", + mesh, + frequencies, + amplitude, + zeta, + HB_ORDER + )) + + with Pool() as p: + p.starmap(simulate_hb, args) + + # Plot + if False: + zeta = 5000 + charges = [] - for amplitude in amplitudes[:1]: - hb_wd = os.path.join(CWD, f"hb_test_{amplitude}") + for amplitude in amplitudes: + hb_wd = os.path.join(CWD, f"hb_test_{amplitude}_{zeta}") charge = np.load(os.path.join(hb_wd, "q.npy")) charges.append(charge) plot_impedances(charges, amplitudes, frequencies) diff --git a/scripts/test_jacobian.py b/scripts/test_jacobian.py new file mode 100644 index 0000000..3302a19 --- /dev/null +++ b/scripts/test_jacobian.py @@ -0,0 +1,77 @@ +"""Module to test the analytical jacobians with ones calculated through +finite differences. +""" + +import os +import plutho + + +# Make random? +pic181 = plutho.MaterialData( + **{ + "c11": 141218192791.12772, + "c12": 82292914124.0279, + "c13": 80362329625.75212, + "c33": 137188538228.6311, + "c44": 29848846049.816402, + "e15": 13.216444117013664, + "e31": -4.979419636068149, + "e33": 14.149818966822629, + "eps11": 1.3327347064648263e-08, + "eps33": 5.380490373139249e-09, + "alpha_m": 0.0, + "alpha_k": 1.289813815258054e-10, + "thermal_conductivity": 1.1, + "heat_capacity": 350, + "temperatures": 25, + "density": 7850 + } +) + + +if __name__ == "__main__": + CWD = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "simulations" + ) + + if not os.path.isdir(CWD): + os.makedirs(CWD) + + # Load/create ring mesh + mesh_file = os.path.join(CWD, "ring_mesh.msh") + if not os.path.exists(mesh_file): + plutho.Mesh.generate_rectangular_mesh( + mesh_file, + width=0.00635, + height=0.001, + x_offset=0.0026, + mesh_size=0.0001 + ) + + ZETA = 1 + HB_ORDER = 3 + + mesh = plutho.Mesh(mesh_file, element_order=1) + + # Create a base nonlinearitz and simulation class + nonlinearity = plutho.Nonlinearity() + nonlinearity.set_cubic_rayleigh(ZETA) + + sim = plutho.NLPiezoHB( + "HB_Test", + mesh, + nonlinearity, + HB_ORDER + ) + + # Set materials + sim.add_material( + material_name="pic181", + material_data=pic181, + physical_group_name="" + ) + + sim.assemble() + + sim.test_jacobian() From 725d1100b084a8c5608e054f045bb6f5c7a0ba6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20H=C3=B6lscher?= Date: Wed, 19 Nov 2025 14:57:51 +0100 Subject: [PATCH 08/22] Cleanup scripts and update nl docs --- plutho/simulations/nonlinear/base.py | 34 ++--- plutho/simulations/nonlinear/piezo_hb.py | 15 +- plutho/simulations/nonlinear/piezo_time.py | 16 +-- scripts/example_nonlinear.py | 21 ++- scripts/hb/compare_impedance.py | 156 --------------------- scripts/hb/run_nonlinear.py | 151 -------------------- 6 files changed, 35 insertions(+), 358 deletions(-) delete mode 100644 scripts/hb/compare_impedance.py delete mode 100644 scripts/hb/run_nonlinear.py diff --git a/plutho/simulations/nonlinear/base.py b/plutho/simulations/nonlinear/base.py index 38e51dc..b55d649 100644 --- a/plutho/simulations/nonlinear/base.py +++ b/plutho/simulations/nonlinear/base.py @@ -249,12 +249,11 @@ def set_quadratic_custom(self, nonlinear_data: npt.NDArray): """Sets a quadratic nonlinearity with a custom material tensor. Parameters: - nonlinear_data: Nonlinear material tensor. + nonlinear_data: Nonlinear material tensor (6x6x6). """ self.nonlinear_type = NonlinearType.QuadraticCustom # Reduce to axisymmetric 4x4x4 matrix - # TODO Update this for order 3 or make it n dimensional voigt_map = [0, 1, 3, 2] nm_reduced = np.zeros(shape=(4, 4, 4)) for i_new, i_old in enumerate(voigt_map): @@ -281,14 +280,13 @@ def set_cubic_custom(self, nonlinear_data: npt.NDArray): """Sets a cubic nonlinearity with a custom nonlinear material tensor Parameters: - nonlinear_data: Nonlinear material tensor. + nonlinear_data: Nonlinear material tensor (6x6x6x6). """ self.nonlinear_type = NonlinearType.CubicCustom - # Reduce to axisymmetric 4x4x4 matrix - # TODO Update this for order 3 or make it n dimensional + # Reduce to axisymmetric 4x4x4x4 matrix voigt_map = [0, 1, 3, 2] - nm_reduced = np.zeros(shape=(4, 4, 4)) + nm_reduced = np.zeros(shape=(4, 4, 4, 4)) for i_new, i_old in enumerate(voigt_map): for j_new, j_old in enumerate(voigt_map): for k_new, k_old in enumerate(voigt_map): @@ -309,12 +307,8 @@ def evaluate_force_vector( """Evaluates the nonlinear force vector based on the previously set nonlinearity and the given displacement field u and the FEM matrices. - Parameters: u: Current displacement field. - m: FEM mass matrix. - c: FEM damping matrix. - k: FEM stiffness matrix. Returns: Vector of nonlinear forces. @@ -327,7 +321,7 @@ def evaluate_force_vector( case NonlinearType.QuadraticRayleigh: return self.ln@(u**2) case NonlinearType.QuadraticCustom: - # Use jit compiler --> wimp + # Use jit compiler --> wimp? """ result = np.zeros(3*number_of_nodes) @@ -344,10 +338,7 @@ def evaluate_force_vector( case NonlinearType.CubicCustom: raise NotImplementedError() - def evaluate_jacobian( - self, - u: npt.NDArray, - ): + def evaluate_jacobian(self, u: npt.NDArray) -> sparse.csc_array: """Evaluates the jacobian of the nonlinear force vector based on the current displacement u and the FEM matrices. @@ -356,6 +347,9 @@ def evaluate_jacobian( m: FEM mass matrix. c: FEM damping matrix. k: FEM stiffness matrix. + + Returns: + Jacobian matrix for set nonlinearity type. """ if self.nonlinear_type is None: raise ValueError("Cannot evaluate jacobian, since no \ @@ -389,7 +383,8 @@ def evaluate_jacobian( raise NotImplementedError() def apply_dirichlet_bc(self, dirichlet_nodes: npt.NDArray): - """Applies the dirichlet boundary conditions on the nonlinear matrix.""" + """Applies the dirichlet boundary conditions on the nonlinear matrix. + """ if self.nonlinear_type is None: raise ValueError("Cannot apply dirichlet boundary \ conditions, since no nonlinear type is set") @@ -406,18 +401,14 @@ def apply_dirichlet_bc(self, dirichlet_nodes: npt.NDArray): def assemble( self, - m: sparse.lil_array, - c: sparse.lil_array, k: sparse.lil_array ): """Assembles the nonlinar FEM matrix. It is saved in self.ln. Parameters: - m: FEM mass matrix. - c: FEM damping matrix. k: FEM stiffness matrix. """ - nodes = self.mesh_data.nodes + nodes, _ = self.mesh_data.nodes number_of_nodes = len(nodes) if self.nonlinear_type is None: @@ -430,6 +421,7 @@ def assemble( self.ln = sparse.lil_array( (3*number_of_nodes, 3*number_of_nodes) ) + # Only take the part for the mechanical field self.ln[:2*number_of_nodes, :2*number_of_nodes] = \ k[:2*number_of_nodes, :2*number_of_nodes]*self.zeta case NonlinearType.QuadraticCustom: diff --git a/plutho/simulations/nonlinear/piezo_hb.py b/plutho/simulations/nonlinear/piezo_hb.py index 55e40f6..e361291 100644 --- a/plutho/simulations/nonlinear/piezo_hb.py +++ b/plutho/simulations/nonlinear/piezo_hb.py @@ -60,7 +60,7 @@ def assemble(self): self.c = c self.k = k - self.nonlinearity.assemble(m, c, k) + self.nonlinearity.assemble(k) def simulate_linear( self, @@ -97,7 +97,7 @@ def simulate_linear( # Calculate system matrix angular_frequency = 2*np.pi*frequency - m, c, k = self.get_linear_system_matrix(m, c, k) + m, c, k = self.linear_system_matrix(m, c, k) s = m*angular_frequency**2+c*angular_frequency+k # Solve system @@ -148,7 +148,7 @@ def simulate( # frequency later # Since its linear it is equal to the tangent matrix for the linear # part - m_tan, c_tan, k_tan = self.get_linear_system_matrix(m, c, k) + m_tan, c_tan, k_tan = self.linear_system_matrix(m, c, k) print("Starting harmonic balancing simulation") for frequency_index, frequency in enumerate(frequencies): @@ -238,7 +238,7 @@ def residual( u, frequency ) - def get_linear_system_matrix( + def linear_system_matrix( self, m_fem: sparse.csc_array, c_fem: sparse.csc_array, @@ -544,7 +544,7 @@ def test_jacobian(self, epsilon: float = 1e-6) -> Tuple[float, float]: k = sparse.random(dof, dof, density=0.3, format='csc') # Analytical jacobian - hb_m, hb_c, hb_k = self.get_linear_system_matrix(m, c, k) + hb_m, hb_c, hb_k = self.linear_system_matrix(m, c, k) hb_s = angular_frequency**2*hb_m+angular_frequency*hb_c+hb_k J_analytical = hb_s + self.tangent_nonlinear(u, frequency) @@ -574,7 +574,10 @@ def test_jacobian(self, epsilon: float = 1e-6) -> Tuple[float, float]: def calculate_charge(self, electrode_name: str, is_complex: bool = True): """Calculate the charge of hb method. Resulting charge is saved in - self.q. + self.q. The cos and sin parts of the HB displacement are combined to + one complex value. The resultin charge has 2 dimensions, the first + dimension is the harmonic order and the second dimension the + frequencies. Parameters: electrode_name: Name of the electrode phyiscal group. diff --git a/plutho/simulations/nonlinear/piezo_time.py b/plutho/simulations/nonlinear/piezo_time.py index 756157c..c825333 100644 --- a/plutho/simulations/nonlinear/piezo_time.py +++ b/plutho/simulations/nonlinear/piezo_time.py @@ -55,7 +55,7 @@ def assemble(self): self.c = c self.k = k - self.nonlinearity.assemble(m, c, k) + self.nonlinearity.assemble(k) def simulate( self, @@ -127,10 +127,7 @@ def residual(next_u, current_u, v, a, f): m@(a1*(next_u-current_u)-a2*v-a3*a) + c@(a4*(next_u-current_u)+a5*v+a6*a) + k@next_u+self.nonlinearity.evaluate_force_vector( - next_u, - m, - c, - k + next_u )-f ) @@ -175,8 +172,6 @@ def residual(next_u, current_u, v, a, f): # Calculate tangential stiffness matrix tangent_matrix = self._calculate_tangent_matrix( u_i, - m, - c, k ) delta_u = linalg.spsolve( @@ -285,8 +280,6 @@ def _apply_dirichlet_bc( def _calculate_tangent_matrix( self, u: npt.NDArray, - m: sparse.csc_array, - c: sparse.csc_array, k: sparse.csc_array ): # TODO Duplicate function in piezo_stationary.py @@ -296,11 +289,8 @@ def _calculate_tangent_matrix( Parameters: u: Current mechanical displacement. k: FEM stiffness matrix. - ln: FEM nonlinear stiffness matrix. """ linear = k - nonlinear = self.nonlinearity.evaluate_jacobian( - u, m, c, k - ) + nonlinear = self.nonlinearity.evaluate_jacobian(u) return (linear + nonlinear).tocsc() diff --git a/scripts/example_nonlinear.py b/scripts/example_nonlinear.py index 345fca6..fd002ba 100644 --- a/scripts/example_nonlinear.py +++ b/scripts/example_nonlinear.py @@ -1,4 +1,4 @@ -"""Implements an example on how to run a staionary nonlinear simulation.""" +"""Implements an example on how to run a time-domain nonlinear simulation.""" # Python standard libraries import os @@ -89,12 +89,12 @@ def simulate_nl_time(CWD, mesh, delta_t, number_of_time_steps): # Simulation parameters GAMMA = 0.5 BETA = 0.25 - ZETA = 10 + ZETA = 100 AMPLITUDE = 10 FREQUENCY = 2.07e6 NEWTON_DAMPING = 0.25 - MAX_ITER = 1000 - TOLERANCE = 5e-9 + MAX_ITER = 100 + TOLERANCE = 1e-10 nonlinearity = plutho.Nonlinearity() nonlinearity.set_cubic_rayleigh(ZETA) @@ -110,7 +110,7 @@ def simulate_nl_time(CWD, mesh, delta_t, number_of_time_steps): sim.add_material( material_name="pic181", material_data=pic181, - physical_group_name="" # Means all elements + physical_group_name="" ) # Set boundary conditions @@ -149,12 +149,11 @@ def simulate_nl_time(CWD, mesh, delta_t, number_of_time_steps): if not os.path.isdir(CWD): os.makedirs(CWD) - print(u[:2*node_count]) - u_file = os.path.join(CWD, "u.npy") q_file = os.path.join(CWD, "q.npy") np.save(u_file, sim.u) - np.save(q_file, sim.q) + if sim.q is not None: + np.save(q_file, sim.q) def plot_displacement_spectrum( @@ -203,14 +202,14 @@ def plot_displacement_spectrum( DELTA_T = 1e-8 NUMBER_OF_TIME_STEPS = 20000 - nl_time_sim_name = "nonlinear_20000_1e8_10" + nl_time_sim_name = "nonlinear_cube_time_20000_1e8_10" nl_time_wd = os.path.join(CWD, nl_time_sim_name) ## Simulate - if False: + if True: # simulate_nonlinear_stationary(CWD) simulate_nl_time(nl_time_wd, mesh, DELTA_T, NUMBER_OF_TIME_STEPS) ## Plot - if True: + if False: plot_displacement_spectrum(nl_time_wd, DELTA_T, NUMBER_OF_TIME_STEPS) diff --git a/scripts/hb/compare_impedance.py b/scripts/hb/compare_impedance.py deleted file mode 100644 index 7fa968a..0000000 --- a/scripts/hb/compare_impedance.py +++ /dev/null @@ -1,156 +0,0 @@ - -# Python standard libraries -import os -import time - -# Third party libraries -import numpy as np -import matplotlib.pyplot as plt - -# Local libraries -import plutho - - -pic181_small_1 = plutho.MaterialData( - **{ - "c11": 145235418881.3833, - "c12": 92239167225.64952, - "c13": 81099198743.00365, - "c33": 129637003240.59453, - "c44": 22909547556.381878, - "e15": 11.117240926254714, - "e31": -5.043538535682103, - "e33": 13.828571335580783, - "eps11": 1.7215323179892597e-09, - "eps33": 5.5430630756095885e-09, - "alpha_m": 11.382565204240533, - "alpha_k": 4.789248360238781e-10, - "thermal_conductivity": 0, - "heat_capacity": 0, - "density": 7850, - "temperatures": [] - } -) - - -def simulate_hb(mesh, frequencies, hb_order): - nonlinearity = plutho.Nonlinearity() - nonlinearity.set_cubic_rayleigh(0) - - # Create simulation - sim = plutho.NLPiezoHB( - "HB_Test_Impedance", - mesh, - nonlinearity, - hb_order - ) - - # Set materials - sim.add_material( - material_name="pic181", - material_data=pic181_small_1, - physical_group_name="" - ) - - sim.add_dirichlet_bc( - plutho.FieldType.PHI, - "Electrode", - np.ones(len(frequencies)) - ) - sim.add_dirichlet_bc( - plutho.FieldType.PHI, - "Ground", - np.zeros(len(frequencies)) - ) - - # Run simulation - sim.assemble() - sim.simulate(frequencies) - sim.calculate_charge("Electrode", is_complex=True) - - return sim.q - - -def simulate_freq(mesh, frequencies): - sim = plutho.PiezoFreq( - simulation_name="pincic_test", - mesh=mesh - ) - - sim.add_material( - material_name="pic181", - material_data=pic181_small_1, - physical_group_name="" - ) - - sim.add_dirichlet_bc( - field_type=plutho.FieldType.PHI, - physical_group_name="Electrode", - values=np.ones(len(frequencies)) - ) - sim.add_dirichlet_bc( - field_type=plutho.FieldType.PHI, - physical_group_name="Ground", - values=np.zeros(len(frequencies)) - ) - - sim.assemble() - sim.simulate( - frequencies, - False - ) - - sim.calculate_charge("Electrode", is_complex=True) - - return sim.q - - -def plot_impedances(charge_freq, charge_hb, frequencies): - impedance_freq = np.abs(1/(1j*2*np.pi*frequencies*charge_freq)) - impedance_hb = np.abs(1/(1j*2*np.pi*frequencies*charge_hb[0, :])) - plt.plot(frequencies/1e6, impedance_freq, label="plutho freq") - plt.plot(frequencies/1e6, impedance_hb, "--", label="plutho hb") - plt.legend() - plt.grid() - plt.show() - - -if __name__ == "__main__": - CWD = os.path.join( - os.path.dirname(os.path.abspath(__file__)), - "..", "..", - "simulations" - ) - - if not os.path.isdir(CWD): - os.makedirs(CWD) - - # Load/create ring mesh - mesh_file = os.path.join(CWD, "ring_mesh.msh") - if not os.path.exists(mesh_file): - plutho.Mesh.generate_rectangular_mesh( - mesh_file, - width=0.00635, - height=0.001, - x_offset=0.0026, - mesh_size=0.0001 - ) - mesh = plutho.Mesh(mesh_file, element_order=1) - - # Simulation parameters - frequencies = np.linspace(0, 1e7, 1000)[1:] - HB_ORDER = 3 - - # Simulate impedance - start_time = time.time() - charge_hb = simulate_hb(mesh, frequencies, HB_ORDER) - plutho_hb_time = time.time() - start_time - - start_time = time.time() - charge_freq = simulate_freq(mesh, frequencies) - plutho_freq_time = time.time() - start_time - - print(f"plutho hb took {plutho_hb_time}s to simulate.") - print(f"plutho freq pincic took {plutho_freq_time}s to simulate.") - - plot_impedances(charge_freq, charge_hb, frequencies) diff --git a/scripts/hb/run_nonlinear.py b/scripts/hb/run_nonlinear.py deleted file mode 100644 index 3fb16bf..0000000 --- a/scripts/hb/run_nonlinear.py +++ /dev/null @@ -1,151 +0,0 @@ -"""Implements an example script for the finite element method harmonic -balacing simulation. -""" - -# Python standard libraries -import os - -# Third party libraries -import numpy as np -import matplotlib.pyplot as plt -from multiprocessing import Pool - -# Local libraries -import plutho - - -pic181_small_1 = plutho.MaterialData( - **{ - "c11": 145235418881.3833, - "c12": 92239167225.64952, - "c13": 81099198743.00365, - "c33": 129637003240.59453, - "c44": 22909547556.381878, - "e15": 11.117240926254714, - "e31": -5.043538535682103, - "e33": 13.828571335580783, - "eps11": 1.7215323179892597e-09, - "eps33": 5.5430630756095885e-09, - "alpha_m": 11.382565204240533, - "alpha_k": 4.789248360238781e-10, - "thermal_conductivity": 0, - "heat_capacity": 0, - "density": 7850, - "temperatures": [] - } -) - - -def simulate_hb(CWD, mesh, frequencies, amplitude, zeta, hb_order): - nonlinearity = plutho.Nonlinearity() - nonlinearity.set_cubic_rayleigh(zeta) - - # Create simulation - sim = plutho.NLPiezoHB( - "HB_Test", - mesh, - nonlinearity, - hb_order - ) - - # Set materials - sim.add_material( - material_name="pic181", - material_data=pic181_small_1, - physical_group_name="" # Means all elements - ) - - sim.add_dirichlet_bc( - plutho.FieldType.PHI, - "Electrode", - amplitude*np.ones(len(frequencies)) - ) - sim.add_dirichlet_bc( - plutho.FieldType.PHI, - "Ground", - np.zeros(len(frequencies)) - ) - - # Run simulation - sim.assemble() - sim.simulate(frequencies) - sim.calculate_charge("Electrode", is_complex=True) - - # Save results - if not os.path.isdir(CWD): - os.makedirs(CWD) - - u_file = os.path.join(CWD, "u.npy") - q_file = os.path.join(CWD, "q.npy") - np.save(u_file, sim.u) - if sim.q is not None: - np.save(q_file, sim.q) - - -def plot_impedances(charges, amplitudes, frequencies): - for charge, amplitude in zip(charges, amplitudes): - impedance = np.abs(amplitude/(1j*2*np.pi*frequencies*charge)) - plt.plot(frequencies/1e6, impedance, label=f"{amplitude} V") - - plt.xlabel("Frequency / kHz") - plt.ylabel("Impedance / $\\Omega$") - plt.legend() - plt.grid() - plt.show() - - -if __name__ == "__main__": - CWD = os.path.join( - os.path.dirname(os.path.abspath(__file__)), - "..", "..", - "simulations" - ) - - if not os.path.isdir(CWD): - os.makedirs(CWD) - - # Load/create ring mesh - mesh_file = os.path.join(CWD, "ring_mesh.msh") - if not os.path.exists(mesh_file): - plutho.Mesh.generate_rectangular_mesh( - mesh_file, - width=0.00635, - height=0.001, - x_offset=0.0026, - mesh_size=0.0001 - ) - mesh = plutho.Mesh(mesh_file, element_order=1) - - # Simulation parameters - HB_ORDER = 3 - frequencies = np.linspace(90e3, 120e3, num=1000) - zetas = [0, 1, 10, 50, 100, 500, 1000, 5000] - amplitudes = [0.7, 3.6, 6.6] - - # Simulate - if True: - args = [] - for zeta in zetas: - for amplitude in amplitudes: - args.append(( - f"hb_test_{amplitude}_{zeta}", - mesh, - frequencies, - amplitude, - zeta, - HB_ORDER - )) - - with Pool() as p: - p.starmap(simulate_hb, args) - - # Plot - if False: - zeta = 5000 - - charges = [] - for amplitude in amplitudes: - hb_wd = os.path.join(CWD, f"hb_test_{amplitude}_{zeta}") - charge = np.load(os.path.join(hb_wd, "q.npy")) - charges.append(charge) - plot_impedances(charges, amplitudes, frequencies) From 0d5fba5c3009f880d4d24487d870622dd4df86a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20H=C3=B6lscher?= Date: Fri, 21 Nov 2025 11:29:21 +0100 Subject: [PATCH 09/22] Add hb newton damping and fix jacobian test --- plutho/materials.py | 3 +- plutho/simulations/nonlinear/base.py | 30 ++--- plutho/simulations/nonlinear/piezo_hb.py | 131 +++++++++++---------- plutho/simulations/nonlinear/piezo_time.py | 11 +- 4 files changed, 91 insertions(+), 84 deletions(-) diff --git a/plutho/materials.py b/plutho/materials.py index aac06a3..808389f 100644 --- a/plutho/materials.py +++ b/plutho/materials.py @@ -47,7 +47,8 @@ def to_dict(self): json_dict = {} for attribute in fields(self.__class__): value = getattr(self, attribute.name) - if isinstance(value, float) or isinstance(value, int): + if isinstance(value, float) or isinstance(value, int) or \ + isinstance(value, list): json_dict[attribute.name] = value elif isinstance(value, np.ndarray): json_dict[attribute.name] = value.tolist() diff --git a/plutho/simulations/nonlinear/base.py b/plutho/simulations/nonlinear/base.py index b55d649..bc9302c 100644 --- a/plutho/simulations/nonlinear/base.py +++ b/plutho/simulations/nonlinear/base.py @@ -321,35 +321,38 @@ def evaluate_force_vector( case NonlinearType.QuadraticRayleigh: return self.ln@(u**2) case NonlinearType.QuadraticCustom: - # Use jit compiler --> wimp? - """ - result = np.zeros(3*number_of_nodes) + # Use jit compiler --> wimp? or inline c? + number_of_nodes = len(self.mesh_data.nodes) + res = np.zeros(3*number_of_nodes) for index in range(3*number_of_nodes): - L_k = L[index*3*number_of_nodes:(index+1)*3*number_of_nodes, :] + L_k = self.ln[ + index*3*number_of_nodes:(index+1)*3*number_of_nodes, + : + ] - result[index] = u @ (L_k @ u) + res[index] = u @ (L_k @ u) - return result - """ - raise NotImplementedError() + return res case NonlinearType.CubicRayleigh: return self.ln@(u**3) case NonlinearType.CubicCustom: raise NotImplementedError() - def evaluate_jacobian(self, u: npt.NDArray) -> sparse.csc_array: + def evaluate_jacobian( + self, + u: npt.NDArray, + k: sparse.csc_array + ) -> sparse.csc_array: """Evaluates the jacobian of the nonlinear force vector based on the current displacement u and the FEM matrices. Parameters: u: Current displacement vector. - m: FEM mass matrix. - c: FEM damping matrix. k: FEM stiffness matrix. Returns: - Jacobian matrix for set nonlinearity type. + Jacobian for nonlinearity. """ if self.nonlinear_type is None: raise ValueError("Cannot evaluate jacobian, since no \ @@ -408,8 +411,7 @@ def assemble( Parameters: k: FEM stiffness matrix. """ - nodes, _ = self.mesh_data.nodes - number_of_nodes = len(nodes) + number_of_nodes = len(self.mesh_data.nodes) if self.nonlinear_type is None: raise ValueError("Cannot assemble matrices, since no \ diff --git a/plutho/simulations/nonlinear/piezo_hb.py b/plutho/simulations/nonlinear/piezo_hb.py index e361291..b365810 100644 --- a/plutho/simulations/nonlinear/piezo_hb.py +++ b/plutho/simulations/nonlinear/piezo_hb.py @@ -107,8 +107,9 @@ def simulate_linear( def simulate( self, frequencies: npt.NDArray, - tolerance: float = 1e-11, - max_iter: int = 300 + tolerance: float = 1e-6, + max_iter: int = 100, + newton_damping: float = 1 ): """Runs the nonlinear simulation at the given frequencies. @@ -116,6 +117,8 @@ def simulate( frequencies: Frequencies for which the simulation is done. tolerance: Tolerance for the residual norm. max_iter: Maximum number of iterations for each frequency. + newton_damping: Multiplied with the newton iteration steps to + reduces the step size. """ dirichlet_nodes = np.array(self.dirichlet_nodes) dirichlet_values = np.array(self.dirichlet_values) @@ -164,62 +167,63 @@ def simulate( dirichlet_values[:, frequency_index] ) - # Get initial value - if frequency_index > 0: - u_i = u[frequency_index-1, :] - else: - u_i = u[frequency_index, :] - - # Check if initial value is already sufficient - residual = self.residual( - tangent_linear, u_i, f, frequency - ) - best_norm = np.linalg.norm(residual) - if best_norm < tolerance: - print("Initial value already sufficient") - u[frequency_index, :] = u_i - continue - best_u_i = u_i - # Newton iteration converged = False - for i in range(max_iter): - # Calculate next guess for u using tangent matrix - tangent_matrix = tangent_linear + self.tangent_nonlinear( - u_i, frequency - ) - - # TODO Faster than slin.spsolve? -> Change in other solvers? - lu = slin.splu(tangent_matrix) - delta_u = lu.solve(residual) - u_i_next = u_i - delta_u - - # Update residual + current_damping = newton_damping + while True: + # Get initial value + if frequency_index > 0: + u_i = u[frequency_index-1, :] + else: + u_i = u[frequency_index, :] + + # Check if initial value is already sufficient residual = self.residual( - tangent_linear, u_i_next, f, frequency + tangent_linear, u_i, f, frequency ) - - # Check for convergence norm = np.linalg.norm(residual) if norm < tolerance: - # Newton converged - u[frequency_index, :] = u_i_next - converged = True - print(f"Frequency step {frequency_index} converged after " - f"iteration {i+1}" - ) + print("Initial value already sufficient") + u[frequency_index, :] = u_i break - elif norm < best_norm: - best_norm = norm - best_u_i = u_i_next - if i % 100 == 0 and i > 0: - print("Iteration:", i) + for i in range(max_iter): + # Calculate next guess for u using tangent matrix + tangent_matrix = tangent_linear + self.tangent_nonlinear( + u_i, frequency + ) + + # TODO Faster than slin.spsolve? -> Change in other solvers? + lu = slin.splu(tangent_matrix) + delta_u = lu.solve(residual) + u_i_next = u_i - delta_u * current_damping - if not converged: - print("Newton did not converge at frequency index " - f"{frequency_index}. Choosing best value: {best_norm}") - u[frequency_index, :] = best_u_i + # Update residual + residual = self.residual( + tangent_linear, u_i_next, f, frequency + ) + + # Check for convergence + norm = np.linalg.norm(residual) + if norm < tolerance: + # Newton converged + u[frequency_index, :] = u_i_next + converged = True + print(f"Frequency step {frequency_index} converged after " + f"iteration {i+1}" + ) + break + + # Update for next iteration + u_i = u_i_next + + if not converged: + print("Tryping again with lower damping at " + f"{frequency_index}") + current_damping *= 0.5 + else: + current_damping = newton_damping + break self.u = u @@ -431,7 +435,7 @@ def tangent_nonlinear( u_time += np.outer(u[(2*n+1)*dof:(2*n+2)*dof], sin_basis[n, :]) # Build jacobian from sub-blocks and calculate dft of df_du_time - df_du_time = u_time ** 2 + df_du_time = 3 * u_time ** 2 blocks = [] for i in range(self.hb_order): # Cos part @@ -441,13 +445,13 @@ def tangent_nonlinear( integrand = df_du_time * cos_basis[i, :] * cos_basis[j, :] cc_diag = (2/N) * np.sum(integrand, axis=1) cc_diag_sparse = sparse.diags_array([cc_diag], offsets=[0]) - cc = 3*self.nonlinearity.ln@cc_diag_sparse + cc = self.nonlinearity.ln@cc_diag_sparse # Cos sin part integrand = df_du_time * cos_basis[i, :] * sin_basis[j, :] cs_diag = (2/N) * np.sum(integrand, axis=1) cs_diag_sparse = sparse.diags_array([cs_diag], offsets=[0]) - cs = 3*self.nonlinearity.ln@cs_diag_sparse + cs = self.nonlinearity.ln@cs_diag_sparse row_blocks.append(cc) row_blocks.append(cs) @@ -461,13 +465,13 @@ def tangent_nonlinear( integrand = df_du_time * sin_basis[i, :] * cos_basis[j, :] sc_diag = (2/N) * np.sum(integrand, axis=1) sc_diag_sparse = sparse.diags_array([sc_diag], offsets=[0]) - sc = 3*self.nonlinearity.ln@sc_diag_sparse + sc = self.nonlinearity.ln@sc_diag_sparse # Sin sin part integrand = df_du_time * sin_basis[i, :] * sin_basis[j, :] ss_diag = (2/N) * np.sum(integrand, axis=1) ss_diag_sparse = sparse.diags_array([ss_diag], offsets=[0]) - ss = 3*self.nonlinearity.ln@ss_diag_sparse + ss = self.nonlinearity.ln@ss_diag_sparse row_blocks.append(sc) row_blocks.append(ss) @@ -535,13 +539,13 @@ def test_jacobian(self, epsilon: float = 1e-6) -> Tuple[float, float]: # Create test data nodes, _ = self.mesh.get_mesh_nodes_and_elements() dof = 3*len(nodes) - frequency = np.random.randn(1)[0] * 1e6 + frequency = np.random.randn(1)[0] * 1e6 + 1 angular_frequency = 2*np.pi*frequency u = np.random.randn(2*self.hb_order*dof) * 0.01 f = np.random.randn(2*self.hb_order*dof) * 0.01 - m = sparse.random(dof, dof, density=0.3, format='csc') - c = sparse.random(dof, dof, density=0.3, format='csc') - k = sparse.random(dof, dof, density=0.3, format='csc') + m = sparse.random(dof, dof, density=0.1, format='csc') + c = sparse.random(dof, dof, density=0.1, format='csc') + k = sparse.random(dof, dof, density=0.1, format='csc') # Analytical jacobian hb_m, hb_c, hb_k = self.linear_system_matrix(m, c, k) @@ -553,12 +557,13 @@ def test_jacobian(self, epsilon: float = 1e-6) -> Tuple[float, float]: J_fd = np.zeros((n, n)) for i in range(n): - u_perturbed = u.copy() - u_perturbed[i] += epsilon - Fp = self.residual(hb_s, u_perturbed, f, frequency) + u_plus = u.copy() + u_plus[i] += epsilon + Fp = self.residual(hb_s, u_plus, f, frequency) - u_perturbed[i] -= epsilon - Fm = self.residual(hb_s, u_perturbed, f, frequency) + u_minus = u.copy() + u_minus[i] -= epsilon + Fm = self.residual(hb_s, u_minus, f, frequency) J_fd[:, i] = (Fp - Fm) / (2*epsilon) diff --git a/plutho/simulations/nonlinear/piezo_time.py b/plutho/simulations/nonlinear/piezo_time.py index c825333..852b87a 100644 --- a/plutho/simulations/nonlinear/piezo_time.py +++ b/plutho/simulations/nonlinear/piezo_time.py @@ -63,8 +63,8 @@ def simulate( number_of_time_steps: int, gamma: float, beta: float, - tolerance: float = 1e-11, - max_iter: int = 300, + tolerance: float = 1e-6, + max_iter: int = 100, newton_damping: float = 1, u_start: Union[npt.NDArray, None] = None ): @@ -77,6 +77,8 @@ def simulate( beta: Newmark integration parameter. tolerance: Tolerance of the newton iteration. max_iter: Maximum number of iterations for newton raphson. + newton_damping: Multiplied with the newton iteration steps to + reduces the step size. u_start: Initial field for u. """ dirichlet_nodes = np.array(self.dirichlet_nodes) @@ -197,9 +199,6 @@ def residual(next_u, current_u, v, a, f): best_norm = norm best_u_i = u_i_next - if i % 100 == 0 and i > 0: - print("Iteration:", i) - # Update for next iteration u_i = u_i_next if not self.converged: @@ -291,6 +290,6 @@ def _calculate_tangent_matrix( k: FEM stiffness matrix. """ linear = k - nonlinear = self.nonlinearity.evaluate_jacobian(u) + nonlinear = self.nonlinearity.evaluate_jacobian(u, k) return (linear + nonlinear).tocsc() From d27315e723daf2517de7b1d79eca3749ae133855 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20H=C3=B6lscher?= Date: Fri, 21 Nov 2025 11:29:42 +0100 Subject: [PATCH 10/22] Update scripts --- scripts/basic_example.py | 1 + scripts/example_nonlinear.py | 29 ++++++++++++++--------------- scripts/test_jacobian.py | 5 +++-- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/scripts/basic_example.py b/scripts/basic_example.py index 402508e..bb0ecae 100644 --- a/scripts/basic_example.py +++ b/scripts/basic_example.py @@ -396,6 +396,7 @@ def simulate_thermo_time(base_directory): if __name__ == "__main__": CWD = os.path.join( os.path.dirname(os.path.abspath(__file__)), + "..", "simulations" ) diff --git a/scripts/example_nonlinear.py b/scripts/example_nonlinear.py index fd002ba..2ead149 100644 --- a/scripts/example_nonlinear.py +++ b/scripts/example_nonlinear.py @@ -89,10 +89,10 @@ def simulate_nl_time(CWD, mesh, delta_t, number_of_time_steps): # Simulation parameters GAMMA = 0.5 BETA = 0.25 - ZETA = 100 + ZETA = 1e12 AMPLITUDE = 10 FREQUENCY = 2.07e6 - NEWTON_DAMPING = 0.25 + NEWTON_DAMPING = 0.8 MAX_ITER = 100 TOLERANCE = 1e-10 @@ -164,8 +164,8 @@ def plot_displacement_spectrum( node_index = 129 u = np.load(os.path.join(working_directory, "u.npy")) - u_r = u[2*node_index, :] - u_z = u[2*node_index+1, :] + u_r = u[:, 2*node_index] + u_z = u[:, 2*node_index+1] U_r_jw = np.fft.fft(u_r) frequencies = np.fft.fftfreq(number_of_time_steps, delta_t) @@ -189,27 +189,26 @@ def plot_displacement_spectrum( # Load/create ring mesh mesh_file = os.path.join(CWD, "ring_mesh.msh") - if not os.path.exists(mesh_file): - plutho.Mesh.generate_rectangular_mesh( - mesh_file, - width=0.00635, - height=0.001, - x_offset=0.0026, - mesh_size=0.0001 - ) + plutho.Mesh.generate_rectangular_mesh( + mesh_file, + width=0.00635, + height=0.001, + x_offset=0.0026, + mesh_size=0.0001 + ) mesh = plutho.Mesh(mesh_file, element_order=1) DELTA_T = 1e-8 NUMBER_OF_TIME_STEPS = 20000 - nl_time_sim_name = "nonlinear_cube_time_20000_1e8_10" + nl_time_sim_name = "nonlinear_time_test" nl_time_wd = os.path.join(CWD, nl_time_sim_name) ## Simulate - if True: + if False: # simulate_nonlinear_stationary(CWD) simulate_nl_time(nl_time_wd, mesh, DELTA_T, NUMBER_OF_TIME_STEPS) ## Plot - if False: + if True: plot_displacement_spectrum(nl_time_wd, DELTA_T, NUMBER_OF_TIME_STEPS) diff --git a/scripts/test_jacobian.py b/scripts/test_jacobian.py index 3302a19..4ae995b 100644 --- a/scripts/test_jacobian.py +++ b/scripts/test_jacobian.py @@ -49,7 +49,7 @@ mesh_size=0.0001 ) - ZETA = 1 + ZETA = 1e12 HB_ORDER = 3 mesh = plutho.Mesh(mesh_file, element_order=1) @@ -74,4 +74,5 @@ sim.assemble() - sim.test_jacobian() + for i in range(10): + sim.test_jacobian() From d2ac7cc2cddb0c99fb5282835dacbd737fbb5ffa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20H=C3=B6lscher?= Date: Fri, 21 Nov 2025 16:00:19 +0100 Subject: [PATCH 11/22] Harmonic balancing stop when sim diverged --- plutho/simulations/nonlinear/piezo_hb.py | 93 ++++++++++++------------ 1 file changed, 45 insertions(+), 48 deletions(-) diff --git a/plutho/simulations/nonlinear/piezo_hb.py b/plutho/simulations/nonlinear/piezo_hb.py index b365810..c15924b 100644 --- a/plutho/simulations/nonlinear/piezo_hb.py +++ b/plutho/simulations/nonlinear/piezo_hb.py @@ -167,63 +167,60 @@ def simulate( dirichlet_values[:, frequency_index] ) + # Get initial value + if frequency_index > 0: + u_i = u[frequency_index-1, :] + else: + u_i = u[frequency_index, :] + + # Check if initial value is already sufficient + residual = self.residual( + tangent_linear, u_i, f, frequency + ) + norm = np.linalg.norm(residual) + if norm < tolerance: + print("Initial value already sufficient") + u[frequency_index, :] = u_i + continue + # Newton iteration converged = False - current_damping = newton_damping - while True: - # Get initial value - if frequency_index > 0: - u_i = u[frequency_index-1, :] - else: - u_i = u[frequency_index, :] - - # Check if initial value is already sufficient + for i in range(max_iter): + # Calculate next guess for u using tangent matrix + tangent_matrix = tangent_linear + self.tangent_nonlinear( + u_i, frequency + ) + + # TODO Faster than slin.spsolve? -> Change in other solvers? + lu = slin.splu(tangent_matrix) + delta_u = lu.solve(residual) + u_i_next = u_i - delta_u * newton_damping + + # Update residual residual = self.residual( - tangent_linear, u_i, f, frequency + tangent_linear, u_i_next, f, frequency ) + + # Check for convergence norm = np.linalg.norm(residual) if norm < tolerance: - print("Initial value already sufficient") - u[frequency_index, :] = u_i - break - - for i in range(max_iter): - # Calculate next guess for u using tangent matrix - tangent_matrix = tangent_linear + self.tangent_nonlinear( - u_i, frequency + # Newton converged + u[frequency_index, :] = u_i_next + converged = True + print(f"Frequency step {frequency_index} converged after " + f"iteration {i+1}" ) + break - # TODO Faster than slin.spsolve? -> Change in other solvers? - lu = slin.splu(tangent_matrix) - delta_u = lu.solve(residual) - u_i_next = u_i - delta_u * current_damping - - # Update residual - residual = self.residual( - tangent_linear, u_i_next, f, frequency - ) + # Update for next iteration + u_i = u_i_next - # Check for convergence - norm = np.linalg.norm(residual) - if norm < tolerance: - # Newton converged - u[frequency_index, :] = u_i_next - converged = True - print(f"Frequency step {frequency_index} converged after " - f"iteration {i+1}" - ) - break - - # Update for next iteration - u_i = u_i_next - - if not converged: - print("Tryping again with lower damping at " - f"{frequency_index}") - current_damping *= 0.5 - else: - current_damping = newton_damping - break + if not converged: + self.u = u + return + print("Not converged, just using previous value at " + f"{frequency_index}") + u[frequency_index, :] = u[frequency_index-1, :] self.u = u From fb46929f828520dd6b5f8ac715a859073c2a7407 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20H=C3=B6lscher?= Date: Tue, 25 Nov 2025 09:37:46 +0100 Subject: [PATCH 12/22] Update nl quadratic --- plutho/simulations/nonlinear/base.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/plutho/simulations/nonlinear/base.py b/plutho/simulations/nonlinear/base.py index bc9302c..9a020f4 100644 --- a/plutho/simulations/nonlinear/base.py +++ b/plutho/simulations/nonlinear/base.py @@ -358,28 +358,27 @@ def evaluate_jacobian( raise ValueError("Cannot evaluate jacobian, since no \ nonlinear type is set") + number_of_nodes = len(self.mesh_data.nodes) + match self.nonlinear_type: case NonlinearType.QuadraticRayleigh: return 2*self.ln@sparse.diags_array(u) case NonlinearType.QuadraticCustom: - """ k_tangent = sparse.lil_matrix(( 3*number_of_nodes, 3*number_of_nodes )) - k_tangent += k - m = np.arange(3*number_of_nodes) # TODO This calculation is very slow for i in range(3*number_of_nodes): - l_k = ln[3*number_of_nodes*i:3*number_of_nodes*(i+1), :] - l_i = ln[3*number_of_nodes*m+i, :] + l_k = self.ln[3*number_of_nodes*i:3*number_of_nodes*(i+1), :] + l_i = self.ln[3*number_of_nodes*m+i, :] k_tangent += (l_k*u[i]).T k_tangent += (l_i*u[i]).T - """ - raise NotImplementedError() + + return k_tangent.tocsc() case NonlinearType.CubicRayleigh: return 3*self.ln@sparse.diags_array(u**2) case NonlinearType.CubicCustom: @@ -392,13 +391,15 @@ def apply_dirichlet_bc(self, dirichlet_nodes: npt.NDArray): raise ValueError("Cannot apply dirichlet boundary \ conditions, since no nonlinear type is set") - # TODO Is a different handling for nonlinear types necessary? + number_of_nodes = len(self.mesh_data.nodes) + match self.nonlinear_type: case NonlinearType.QuadraticRayleigh | \ NonlinearType.CubicRayleigh: self.ln[dirichlet_nodes, :] = 0 case NonlinearType.QuadraticCustom: - raise NotImplementedError() + for i in range(3*number_of_nodes): + self.ln[dirichlet_nodes+i*3*number_of_nodes, :] = 0 case NonlinearType.CubicCustom: raise NotImplementedError() From 8b33cd0b07a88552d4a9b4837f60ab948c7c2ac0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20H=C3=B6lscher?= Date: Tue, 25 Nov 2025 12:26:46 +0100 Subject: [PATCH 13/22] Add RIKS arc-length to HB --- plutho/simulations/nonlinear/piezo_hb.py | 254 ++++++++++++++++++----- plutho/simulations/solver.py | 1 + 2 files changed, 199 insertions(+), 56 deletions(-) diff --git a/plutho/simulations/nonlinear/piezo_hb.py b/plutho/simulations/nonlinear/piezo_hb.py index c15924b..2db4ec5 100644 --- a/plutho/simulations/nonlinear/piezo_hb.py +++ b/plutho/simulations/nonlinear/piezo_hb.py @@ -2,7 +2,7 @@ harmonic balancing method.""" # Python standard libraries -from typing import Tuple +from typing import Tuple, Callable # Third party libraries import numpy as np @@ -104,6 +104,153 @@ def simulate_linear( lu = slin.splu(s) return lu.solve(f) + @staticmethod + def newton_arclength( + u_init: npt.NDArray, + P: npt.NDArray, + rhs_fun: Callable, + tangent_fun: Callable, + max_iter: int, + tolerance: float, + u_last: npt.NDArray, + load_last: float + ): + # Set initial value + u_0 = u_init + arc_length = 0 + + # Check if initial value is already sufficient + residual = rhs_fun(u_0) + norm = np.linalg.norm(residual) + if norm < tolerance: + print("Initial value already sufficient") + return u_0 + + # Residual function + def G(u, load): + return rhs_fun(u) - load*P + + # RIKS arc-length constraint + def riks(u, u_prev, load, load_prev): + return (u_0-u_prev).T@(u-u_0)+(load_0-load_prev)*(load-load_0) + + # RIKS arc-length constrained derived after load + def riks_dload(load_prev): + return load_0-load_prev + + # RIKS arc-length constrained derived after u + def riks_du(u_prev): + return u_0-u_prev + + converged = False + while not converged: + # Predictor step + k_t = tangent_fun(u_0) + lu = slin.splu(k_t) + u_p_0 = lu.solve(P) * 0.1 # Scale down initial predictor + load_0 = load_last + arc_length/np.linalg.norm(u_p_0) + + # Initial guess for arc_length + if arc_length == 0: + arc_length = 0.01 * np.linalg.norm(u_p_0) + else: + arc_length *= 0.25 + + # Set iteration variables to intials + u_i = u_0 + load_i = load_0 + + # Iterate + for i in range(max_iter): + print(f"Load {load_i}") + # Compute increments + u_p_next = lu.solve(P) + u_g_next = lu.solve(-G(u_i, load_i)) + + delta_load_i = - ( + riks(u_i, u_last, load_i, load_last) + + riks_du(u_last)@u_g_next + )/( + riks_dload(load_last) + + riks_du(u_last)@u_p_next + ) + delta_u_i = delta_load_i*u_p_next+u_g_next + + # Increment + load_i_next = load_i + delta_load_i + u_i_next = u_i + delta_u_i + + # Update residual + residual = G(u_i_next, load_i_next) + + # Check for convergence + norm = np.linalg.norm(residual) + if norm < tolerance: + # Newton converged + print(f"Newton converged after {i+1} iterations") + return u_i_next, load_i_next + elif np.abs(norm) > 1e20: + break + + # Update for next iteration + u_i = u_i_next + load_i = load_i_next + lu = slin.splu(tangent_fun(u_i)) + + print(f"Newton did not converged: Maximum iteration ({norm})" + " retrying with lower arc-length" + ) + + print(f"Newton did not converge: Maximum iteration ({norm})") + return u_i, load_i + + @staticmethod + def newton( + u_init: npt.NDArray, + residual_fun: Callable, + tangent_fun: Callable, + max_iter: int, + tolerance: float, + newton_damping: float + ): + # Set initial value + u_i = u_init + + # Check if initial value is already sufficient + residual = residual_fun(u_i) + norm = np.linalg.norm(residual) + if norm < tolerance: + print("Initial value already sufficient") + return u_i + + # Run the iteration + converged = False + for i in range(max_iter): + # Calculate next guess for u using tangent matrix + tangent_matrix = tangent_fun(u_i) + + # TODO Faster than slin.spsolve? -> Change in other solvers? + lu = slin.splu(tangent_matrix) + delta_u = lu.solve(residual) + u_i_next = u_i - delta_u * newton_damping + + # Update residual + residual = residual_fun(u_i) + + # Check for convergence + norm = np.linalg.norm(residual) + if norm < tolerance: + # Newton converged + print(f"Newton converged after {i} iterations") + return u_i_next + + # Update for next iteration + u_i = u_i_next + + if not converged: + print(f"Newton did not converge: Maximum iteration ({norm})") + return u_i + def simulate( self, frequencies: npt.NDArray, @@ -141,11 +288,7 @@ def simulate( u = np.zeros( (len(frequencies), 2*3*self.hb_order*number_of_nodes) ) - u[0, :] = self.simulate_linear( - frequencies[0], - dirichlet_nodes, - dirichlet_values[:, 0] - ) + loads = np.zeros(len(frequencies)) # Linear tangent matrix can be created beforehand and scaled with # frequency later @@ -155,6 +298,7 @@ def simulate( print("Starting harmonic balancing simulation") for frequency_index, frequency in enumerate(frequencies): + print(f"Frequency index: {frequency_index}") # Construct whole linear tangent matrix using the current frequency angular_frequency = 2*np.pi*frequency tangent_linear = ( @@ -162,65 +306,62 @@ def simulate( angular_frequency**2 * m_tan ).tocsc() + # Get load vector f = self._get_load_vector( dirichlet_nodes, dirichlet_values[:, frequency_index] ) - # Get initial value - if frequency_index > 0: - u_i = u[frequency_index-1, :] - else: - u_i = u[frequency_index, :] - - # Check if initial value is already sufficient - residual = self.residual( - tangent_linear, u_i, f, frequency - ) - norm = np.linalg.norm(residual) - if norm < tolerance: - print("Initial value already sufficient") - u[frequency_index, :] = u_i - continue - - # Newton iteration - converged = False - for i in range(max_iter): - # Calculate next guess for u using tangent matrix - tangent_matrix = tangent_linear + self.tangent_nonlinear( - u_i, frequency + # Define residual and tangent functions for newton + def residual_fun(u): + return self.residual( + tangent_linear, + u, + f, + frequency ) + def rhs_arc_len_fun(u): + return residual_fun(u) + f - # TODO Faster than slin.spsolve? -> Change in other solvers? - lu = slin.splu(tangent_matrix) - delta_u = lu.solve(residual) - u_i_next = u_i - delta_u * newton_damping - - # Update residual - residual = self.residual( - tangent_linear, u_i_next, f, frequency + def tangent_fun(u): + return tangent_linear + self.tangent_nonlinear( + u, frequency ) - # Check for convergence - norm = np.linalg.norm(residual) - if norm < tolerance: - # Newton converged - u[frequency_index, :] = u_i_next - converged = True - print(f"Frequency step {frequency_index} converged after " - f"iteration {i+1}" - ) - break - - # Update for next iteration - u_i = u_i_next + # Get initial value + if frequency_index > 0: + u_init = u[frequency_index-1, :] + else: + u_init = self.simulate_linear( + frequencies[0], + dirichlet_nodes, + dirichlet_values[:, 0] + ) - if not converged: - self.u = u - return - print("Not converged, just using previous value at " - f"{frequency_index}") - u[frequency_index, :] = u[frequency_index-1, :] + # Solve newton normal + """ + u[frequency_index, :] = NLPiezoHB.newton( + u_init, + residual_fun, + tangent_fun, + max_iter, + tolerance, + newton_damping + ) + """ + # Solve newton arclen + u_res, load_res = NLPiezoHB.newton_arclength( + u_init, + f, + rhs_arc_len_fun, + tangent_fun, + max_iter, + tolerance, + u[frequency_index-1, :], + loads[frequency_index-1] + ) + u[frequency_index, :] = u_res + loads[frequency_index] = load_res self.u = u @@ -498,7 +639,7 @@ def _get_load_vector( # charge density is 0. f = np.zeros(2*self.hb_order*3*number_of_nodes, dtype=np.float64) - # TODO Right now only the base frequency cosine part is set + # TODO: Right now only the base frequency cosine part is set # Set dirichlet bc f[nodes] = values @@ -533,6 +674,7 @@ def test_jacobian(self, epsilon: float = 1e-6) -> Tuple[float, float]: Returns: Tuple of absolute and relative errors. """ + # TODO: Rework and make class independent # Create test data nodes, _ = self.mesh.get_mesh_nodes_and_elements() dof = 3*len(nodes) diff --git a/plutho/simulations/solver.py b/plutho/simulations/solver.py index b87eda2..e04923d 100644 --- a/plutho/simulations/solver.py +++ b/plutho/simulations/solver.py @@ -168,6 +168,7 @@ def add_dirichlet_bc( elif field_type is FieldType.U_Z: real_index = 2*node_index+1 + # TODO: Flip the indices in dirichlet_values: [freq, nodes] self.dirichlet_nodes.append(real_index) self.dirichlet_values.append(values) From 730c237266936bdd5e797d754333f25cde1405f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20H=C3=B6lscher?= Date: Tue, 25 Nov 2025 16:11:31 +0100 Subject: [PATCH 14/22] Arc-length change from load factor to freq factor --- plutho/simulations/nonlinear/piezo_hb.py | 321 +++++++++++++++-------- plutho/simulations/solver.py | 4 + 2 files changed, 220 insertions(+), 105 deletions(-) diff --git a/plutho/simulations/nonlinear/piezo_hb.py b/plutho/simulations/nonlinear/piezo_hb.py index 2db4ec5..29513a6 100644 --- a/plutho/simulations/nonlinear/piezo_hb.py +++ b/plutho/simulations/nonlinear/piezo_hb.py @@ -66,7 +66,7 @@ def simulate_linear( self, frequency: float, dirichlet_nodes: npt.NDArray, - dirichlet_values: npt.NDArray + amplitude: float ) -> npt.NDArray: """Runs a linear simulation for one frequency and returns the whole u vector. @@ -92,7 +92,7 @@ def simulate_linear( # Prepare arrays f = self._get_load_vector( dirichlet_nodes, - dirichlet_values + amplitude ) # Calculate system matrix @@ -104,106 +104,6 @@ def simulate_linear( lu = slin.splu(s) return lu.solve(f) - @staticmethod - def newton_arclength( - u_init: npt.NDArray, - P: npt.NDArray, - rhs_fun: Callable, - tangent_fun: Callable, - max_iter: int, - tolerance: float, - u_last: npt.NDArray, - load_last: float - ): - # Set initial value - u_0 = u_init - arc_length = 0 - - # Check if initial value is already sufficient - residual = rhs_fun(u_0) - norm = np.linalg.norm(residual) - if norm < tolerance: - print("Initial value already sufficient") - return u_0 - - # Residual function - def G(u, load): - return rhs_fun(u) - load*P - - # RIKS arc-length constraint - def riks(u, u_prev, load, load_prev): - return (u_0-u_prev).T@(u-u_0)+(load_0-load_prev)*(load-load_0) - - # RIKS arc-length constrained derived after load - def riks_dload(load_prev): - return load_0-load_prev - - # RIKS arc-length constrained derived after u - def riks_du(u_prev): - return u_0-u_prev - - converged = False - while not converged: - # Predictor step - k_t = tangent_fun(u_0) - lu = slin.splu(k_t) - u_p_0 = lu.solve(P) * 0.1 # Scale down initial predictor - load_0 = load_last + arc_length/np.linalg.norm(u_p_0) - - # Initial guess for arc_length - if arc_length == 0: - arc_length = 0.01 * np.linalg.norm(u_p_0) - else: - arc_length *= 0.25 - - # Set iteration variables to intials - u_i = u_0 - load_i = load_0 - - # Iterate - for i in range(max_iter): - print(f"Load {load_i}") - # Compute increments - u_p_next = lu.solve(P) - u_g_next = lu.solve(-G(u_i, load_i)) - - delta_load_i = - ( - riks(u_i, u_last, load_i, load_last) - + riks_du(u_last)@u_g_next - )/( - riks_dload(load_last) - + riks_du(u_last)@u_p_next - ) - delta_u_i = delta_load_i*u_p_next+u_g_next - - # Increment - load_i_next = load_i + delta_load_i - u_i_next = u_i + delta_u_i - - # Update residual - residual = G(u_i_next, load_i_next) - - # Check for convergence - norm = np.linalg.norm(residual) - if norm < tolerance: - # Newton converged - print(f"Newton converged after {i+1} iterations") - return u_i_next, load_i_next - elif np.abs(norm) > 1e20: - break - - # Update for next iteration - u_i = u_i_next - load_i = load_i_next - lu = slin.splu(tangent_fun(u_i)) - - print(f"Newton did not converged: Maximum iteration ({norm})" - " retrying with lower arc-length" - ) - - print(f"Newton did not converge: Maximum iteration ({norm})") - return u_i, load_i - @staticmethod def newton( u_init: npt.NDArray, @@ -251,6 +151,217 @@ def newton( print(f"Newton did not converge: Maximum iteration ({norm})") return u_i + @staticmethod + def newton_arclength( + u_last: npt.NDArray, + freq_last: float, + freq_load_last: float, + load_vector: npt.NDArray, + tangent_fun: Callable, + rhs_fun: Callable, + tolerance: float, + max_iter: int + ): + # Set initial value + u_0 = u_last + + # Predictor step + lu = slin.splu(tangent_fun(u_0, freq_last, freq_load_last)) + delta_u_p_0 = lu.solve(load_vector) + u_p = u_last + delta_u_p_0 + + # Compute frequency load increment + arc_length = 1000 * np.linalg.norm(u_p) + delta_freq_load_0 = arc_length/(np.linalg.norm(delta_u_p_0)) + ## Calculate direction + current_stiffness_parameter = load_vector@u_p/np.linalg.norm(u_p) + if current_stiffness_parameter < 0: + delta_freq_load_0 *= -1 + freq_load_0 = freq_load_last + delta_freq_load_0 + + # Check if initial value is already sufficient + residual = rhs_fun(u_0, freq_last, freq_load_0) + norm = np.linalg.norm(residual) + if norm < tolerance: + print("Initial value already sufficient") + return u_0 + + # Set initial values for iteraion + u_i = u_0 + freq_load_i = freq_load_0 + for i in range(max_iter): + # Residual function + def G(u, freq_load): + return rhs_fun(u, freq_last, freq_load) - load_vector + + # RIKS arc-length constraint + def riks(u, freq_load): + return ( + (u_0-u_last).T@(u-u_0) + + (freq_load_0-freq_load_last)*(freq_load-freq_load_0) + ) + + # RIKS arc-length constrained derived after load + def riks_dload(): + return freq_load_0-freq_load_last + + # RIKS arc-length constrained derived after u + def riks_du(): + return u_0-u_last + + # Solve for increments + u_p_next = lu.solve(load_vector) + u_g_next = lu.solve(-G(u_i, freq_load_i)) + + # Calculate increments + denominator = riks_dload()+riks_du()@u_p_next + print(denominator) + if denominator < 1e-14: + print("Singular system") + break + delta_freq_load_i = - ( + riks(u_i, freq_load_i) + + riks_du()@u_g_next + )/denominator + delta_u_i = delta_freq_load_i*u_p_next+u_g_next + + # Increment + freq_load_i_next = freq_load_i + delta_freq_load_i + u_i_next = u_i + delta_u_i + + # Update residual + residual = G(u_i_next, freq_load_i_next) + + # Check for convergence + norm = np.linalg.norm(residual) + if norm < tolerance: + # Newton converged + print(f"Newton converged after {i+1} iterations") + return u_i_next, freq_load_i_next + elif np.abs(norm) > 1e10: + print("Newton diverged") + break + + # Update for next iteration + u_i = u_i_next + freq_load_i = freq_load_i_next + lu = slin.splu(tangent_fun(u_i, freq_last, freq_load_i)) + + print(f"Newton did not converged: Maximum iteration (norm: {norm})") + return u_i, freq_load_i + + def simulate_path( + self, + frequency_start: float, + frequency_steps: int, + frequency_default_step: float, + amplitude: float, + tolerance: float = 1e-6, + max_iter: int = 100, + ): + dirichlet_nodes = np.array(self.dirichlet_nodes) + number_of_nodes = len(self.mesh_data.nodes) + + m = self.m.copy() + c = self.c.copy() + k = self.k.copy() + + # Apply dirichlet bc to matrices + m, c, k = self._apply_dirichlet_bc( + m, + c, + k, + dirichlet_nodes + ) + self.nonlinearity.apply_dirichlet_bc(dirichlet_nodes) + + # Prepare array and guess initial value using linear simulation + u = np.zeros( + (frequency_steps, 2*3*self.hb_order*number_of_nodes) + ) + u[0, :] = self.simulate_linear( + frequency_start, + dirichlet_nodes, + amplitude + ) + frequency_loads = np.zeros(frequency_steps) + frequency_loads[0] = 1 + frequencies = np.zeros(frequency_steps+1) + frequencies[0] = frequency_start + + # Linear tangent matrix can be created beforehand and scaled with + # frequency later + # Since its linear it is equal to the tangent matrix for the linear + # part + m_tan, c_tan, k_tan = self.linear_system_matrix(m, c, k) + + print("Starting harmonic balancing simulation") + for index in range(frequency_steps): + frequency = frequencies[index] + print(f"Frequency index: {index}, Frequency: {frequency}") + + # Get load vector + load_vector = self._get_load_vector( + dirichlet_nodes, + amplitude + ) + + # Define residual and tangent functions for newton + def residual_fun(u, frequency, frequency_load): + angular_frequency = 2*np.pi*frequency*frequency_load + tangent_linear = ( + k_tan + + angular_frequency * c_tan + + angular_frequency**2 * m_tan + ) + + return self.residual( + tangent_linear, + u, + load_vector, + frequency + ) + + def rhs_arc_len_fun(u, frequency, frequency_load): + return residual_fun(u, frequency, frequency_load) + load_vector + + def tangent_fun(u, frequency, frequency_load): + angular_frequency = 2*np.pi*frequency*frequency_load + return ( + k_tan + + angular_frequency * c_tan + + angular_frequency**2 * m_tan + + self.tangent_nonlinear(u, frequency) + ).tocsc() + + if index > 0: + u_last = u[index-1, :] + else: + u_last = self.simulate_linear( + frequency, + dirichlet_nodes, + amplitude + ) + + # Solve newton arclen + u_res, frequency_load = NLPiezoHB.newton_arclength( + u_last, + frequencies[index], + frequency_loads[index], + load_vector, + tangent_fun, + rhs_arc_len_fun, + tolerance, + max_iter + ) + u[index, :] = u_res + frequency_loads[index] = frequency_load + frequencies[index+1] = frequency_load*frequency + + self.u = u + self.frequencies = frequencies[:-1] + + def simulate( self, frequencies: npt.NDArray, @@ -335,7 +446,7 @@ def tangent_fun(u): u_init = self.simulate_linear( frequencies[0], dirichlet_nodes, - dirichlet_values[:, 0] + amplitude ) # Solve newton normal @@ -621,7 +732,7 @@ def tangent_nonlinear( def _get_load_vector( self, nodes: npt.NDArray, - values: npt.NDArray + amplitude: float ) -> npt.NDArray: """Calculates the load vector (right hand side) vector for the simulation. @@ -641,7 +752,7 @@ def _get_load_vector( # TODO: Right now only the base frequency cosine part is set # Set dirichlet bc - f[nodes] = values + f[nodes] = amplitude return f diff --git a/plutho/simulations/solver.py b/plutho/simulations/solver.py index e04923d..d16a567 100644 --- a/plutho/simulations/solver.py +++ b/plutho/simulations/solver.py @@ -239,6 +239,10 @@ def save_simulation_results(self, directory: str, prefix: str = ""): q_path = os.path.join(wd, f"{prefix}q.npy") np.save(q_path, self.q) + if self.frequencies is not None: + f_path = os.path.join(wd, f"{prefix}frequencies.npy") + np.save(f_path, self.frequencies) + match self.solver_type: case SolverType.ThermoPiezoTime: mech_loss_path = os.path.join(wd, f"{prefix}mech_loss.npy") From 3cde8ff79b3562ecf75d487d5fa71285a29b6bd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20H=C3=B6lscher?= Date: Thu, 15 Jan 2026 13:38:59 +0100 Subject: [PATCH 15/22] Update pyproject.toml --- pyproject.toml | 37 +++---- uv.lock | 280 ++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 231 insertions(+), 86 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index cf951bd..507e384 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,14 +3,13 @@ name = "plutho" description = "Python library for coupled thermo-piezoelectric simulations" dynamic = ["version"] authors = [ - { name="Jonas Hoelscher", email="hoelscher@emt.uni-paderborn.de" } + { name = "Jonas Hoelscher", email = "hoelscher@emt.uni-paderborn.de" } ] -license = { file = "LICENSE" } +license-files = ["LICENSE"] readme = "README.md" classifiers = [ "Developement Status :: 3 - Alpha", "Intended Audience :: Science/Research", - "License :: OSI Approved ::: BSD License", "Programming Language ::; Python :: 3", "Topic :: Scientific/Engineering" ] @@ -22,18 +21,6 @@ dependencies = [ "gmsh==4.13.1", "python-dotenv", "pyyaml", - "flake8>=7.2.0", - "ruff>=0.12.0", -] - -[project.optional-dependencies] -doc = [ - "setuptools_scm", - "sphinx", - "sphinx-rtd-theme" -] -test = [ - "pytest" ] [proect.urls] @@ -43,13 +30,19 @@ homepage = "https://emt.uni-paderborn.de" requires = ["setuptools>=41", "wheel", "setuptools_scm"] build-backend = "setuptools.build_meta" -[tool.setuptools_scm] - -[tool.uv.sources] -plutho = { workspace = true } +[tool.setuptools.packages.find] +where = ["."] +include = ["plutho"] [dependency-groups] -dev = [ - "plutho", - "pytest>=8.4.1", +test = [ + "pytest" +] +lint = [ + "ruff" +] +ruff = [ + "setuptools_scm", + "sphinx", + "sphinx-rtd-theme" ] diff --git a/uv.lock b/uv.lock index 3f5a516..cc36d56 100644 --- a/uv.lock +++ b/uv.lock @@ -1,6 +1,6 @@ version = 1 -revision = 2 -requires-python = ">=3.10, <3.13" +revision = 3 +requires-python = ">=3.10" resolution-markers = [ "python_full_version >= '3.11'", "python_full_version < '3.11'", @@ -78,6 +78,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/77/1a/5eefc0ce04affb98af07bc05f3bac9094513c0e23b0562d64af46a06aae4/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f", size = 149565, upload-time = "2025-05-02T08:32:51.404Z" }, { url = "https://files.pythonhosted.org/packages/37/a0/2410e5e6032a174c95e0806b1a6585eb21e12f445ebe239fac441995226a/charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c", size = 98357, upload-time = "2025-05-02T08:32:53.079Z" }, { url = "https://files.pythonhosted.org/packages/6c/4f/c02d5c493967af3eda9c771ad4d2bbc8df6f99ddbeb37ceea6e8716a32bc/charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e", size = 105776, upload-time = "2025-05-02T08:32:54.573Z" }, + { url = "https://files.pythonhosted.org/packages/ea/12/a93df3366ed32db1d907d7593a94f1fe6293903e3e92967bebd6950ed12c/charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0", size = 199622, upload-time = "2025-05-02T08:32:56.363Z" }, + { url = "https://files.pythonhosted.org/packages/04/93/bf204e6f344c39d9937d3c13c8cd5bbfc266472e51fc8c07cb7f64fcd2de/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf", size = 143435, upload-time = "2025-05-02T08:32:58.551Z" }, + { url = "https://files.pythonhosted.org/packages/22/2a/ea8a2095b0bafa6c5b5a55ffdc2f924455233ee7b91c69b7edfcc9e02284/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e", size = 153653, upload-time = "2025-05-02T08:33:00.342Z" }, + { url = "https://files.pythonhosted.org/packages/b6/57/1b090ff183d13cef485dfbe272e2fe57622a76694061353c59da52c9a659/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1", size = 146231, upload-time = "2025-05-02T08:33:02.081Z" }, + { url = "https://files.pythonhosted.org/packages/e2/28/ffc026b26f441fc67bd21ab7f03b313ab3fe46714a14b516f931abe1a2d8/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c", size = 148243, upload-time = "2025-05-02T08:33:04.063Z" }, + { url = "https://files.pythonhosted.org/packages/c0/0f/9abe9bd191629c33e69e47c6ef45ef99773320e9ad8e9cb08b8ab4a8d4cb/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691", size = 150442, upload-time = "2025-05-02T08:33:06.418Z" }, + { url = "https://files.pythonhosted.org/packages/67/7c/a123bbcedca91d5916c056407f89a7f5e8fdfce12ba825d7d6b9954a1a3c/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0", size = 145147, upload-time = "2025-05-02T08:33:08.183Z" }, + { url = "https://files.pythonhosted.org/packages/ec/fe/1ac556fa4899d967b83e9893788e86b6af4d83e4726511eaaad035e36595/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b", size = 153057, upload-time = "2025-05-02T08:33:09.986Z" }, + { url = "https://files.pythonhosted.org/packages/2b/ff/acfc0b0a70b19e3e54febdd5301a98b72fa07635e56f24f60502e954c461/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff", size = 156454, upload-time = "2025-05-02T08:33:11.814Z" }, + { url = "https://files.pythonhosted.org/packages/92/08/95b458ce9c740d0645feb0e96cea1f5ec946ea9c580a94adfe0b617f3573/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b", size = 154174, upload-time = "2025-05-02T08:33:13.707Z" }, + { url = "https://files.pythonhosted.org/packages/78/be/8392efc43487ac051eee6c36d5fbd63032d78f7728cb37aebcc98191f1ff/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148", size = 149166, upload-time = "2025-05-02T08:33:15.458Z" }, + { url = "https://files.pythonhosted.org/packages/44/96/392abd49b094d30b91d9fbda6a69519e95802250b777841cf3bda8fe136c/charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7", size = 98064, upload-time = "2025-05-02T08:33:17.06Z" }, + { url = "https://files.pythonhosted.org/packages/e9/b0/0200da600134e001d91851ddc797809e2fe0ea72de90e09bec5a2fbdaccb/charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980", size = 105641, upload-time = "2025-05-02T08:33:18.753Z" }, { url = "https://files.pythonhosted.org/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626, upload-time = "2025-05-02T08:34:40.053Z" }, ] @@ -130,6 +143,26 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2a/8a/bebe5a3f68b484d3a2b8ffaf84704b3e343ef1addea528132ef148e22b3b/contourpy-1.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3d80b2c0300583228ac98d0a927a1ba6a2ba6b8a742463c564f1d419ee5b211e", size = 1380480, upload-time = "2025-04-15T17:38:06.7Z" }, { url = "https://files.pythonhosted.org/packages/34/db/fcd325f19b5978fb509a7d55e06d99f5f856294c1991097534360b307cf1/contourpy-1.3.2-cp312-cp312-win32.whl", hash = "sha256:90df94c89a91b7362e1142cbee7568f86514412ab8a2c0d0fca72d7e91b62912", size = 178489, upload-time = "2025-04-15T17:38:10.338Z" }, { url = "https://files.pythonhosted.org/packages/01/c8/fadd0b92ffa7b5eb5949bf340a63a4a496a6930a6c37a7ba0f12acb076d6/contourpy-1.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:8c942a01d9163e2e5cfb05cb66110121b8d07ad438a17f9e766317bcb62abf73", size = 223042, upload-time = "2025-04-15T17:38:14.239Z" }, + { url = "https://files.pythonhosted.org/packages/2e/61/5673f7e364b31e4e7ef6f61a4b5121c5f170f941895912f773d95270f3a2/contourpy-1.3.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:de39db2604ae755316cb5967728f4bea92685884b1e767b7c24e983ef5f771cb", size = 271630, upload-time = "2025-04-15T17:38:19.142Z" }, + { url = "https://files.pythonhosted.org/packages/ff/66/a40badddd1223822c95798c55292844b7e871e50f6bfd9f158cb25e0bd39/contourpy-1.3.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3f9e896f447c5c8618f1edb2bafa9a4030f22a575ec418ad70611450720b5b08", size = 255670, upload-time = "2025-04-15T17:38:23.688Z" }, + { url = "https://files.pythonhosted.org/packages/1e/c7/cf9fdee8200805c9bc3b148f49cb9482a4e3ea2719e772602a425c9b09f8/contourpy-1.3.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71e2bd4a1c4188f5c2b8d274da78faab884b59df20df63c34f74aa1813c4427c", size = 306694, upload-time = "2025-04-15T17:38:28.238Z" }, + { url = "https://files.pythonhosted.org/packages/dd/e7/ccb9bec80e1ba121efbffad7f38021021cda5be87532ec16fd96533bb2e0/contourpy-1.3.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de425af81b6cea33101ae95ece1f696af39446db9682a0b56daaa48cfc29f38f", size = 345986, upload-time = "2025-04-15T17:38:33.502Z" }, + { url = "https://files.pythonhosted.org/packages/dc/49/ca13bb2da90391fa4219fdb23b078d6065ada886658ac7818e5441448b78/contourpy-1.3.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:977e98a0e0480d3fe292246417239d2d45435904afd6d7332d8455981c408b85", size = 318060, upload-time = "2025-04-15T17:38:38.672Z" }, + { url = "https://files.pythonhosted.org/packages/c8/65/5245ce8c548a8422236c13ffcdcdada6a2a812c361e9e0c70548bb40b661/contourpy-1.3.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:434f0adf84911c924519d2b08fc10491dd282b20bdd3fa8f60fd816ea0b48841", size = 322747, upload-time = "2025-04-15T17:38:43.712Z" }, + { url = "https://files.pythonhosted.org/packages/72/30/669b8eb48e0a01c660ead3752a25b44fdb2e5ebc13a55782f639170772f9/contourpy-1.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c66c4906cdbc50e9cba65978823e6e00b45682eb09adbb78c9775b74eb222422", size = 1308895, upload-time = "2025-04-15T17:39:00.224Z" }, + { url = "https://files.pythonhosted.org/packages/05/5a/b569f4250decee6e8d54498be7bdf29021a4c256e77fe8138c8319ef8eb3/contourpy-1.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8b7fc0cd78ba2f4695fd0a6ad81a19e7e3ab825c31b577f384aa9d7817dc3bef", size = 1379098, upload-time = "2025-04-15T17:43:29.649Z" }, + { url = "https://files.pythonhosted.org/packages/19/ba/b227c3886d120e60e41b28740ac3617b2f2b971b9f601c835661194579f1/contourpy-1.3.2-cp313-cp313-win32.whl", hash = "sha256:15ce6ab60957ca74cff444fe66d9045c1fd3e92c8936894ebd1f3eef2fff075f", size = 178535, upload-time = "2025-04-15T17:44:44.532Z" }, + { url = "https://files.pythonhosted.org/packages/12/6e/2fed56cd47ca739b43e892707ae9a13790a486a3173be063681ca67d2262/contourpy-1.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:e1578f7eafce927b168752ed7e22646dad6cd9bca673c60bff55889fa236ebf9", size = 223096, upload-time = "2025-04-15T17:44:48.194Z" }, + { url = "https://files.pythonhosted.org/packages/54/4c/e76fe2a03014a7c767d79ea35c86a747e9325537a8b7627e0e5b3ba266b4/contourpy-1.3.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0475b1f6604896bc7c53bb070e355e9321e1bc0d381735421a2d2068ec56531f", size = 285090, upload-time = "2025-04-15T17:43:34.084Z" }, + { url = "https://files.pythonhosted.org/packages/7b/e2/5aba47debd55d668e00baf9651b721e7733975dc9fc27264a62b0dd26eb8/contourpy-1.3.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c85bb486e9be652314bb5b9e2e3b0d1b2e643d5eec4992c0fbe8ac71775da739", size = 268643, upload-time = "2025-04-15T17:43:38.626Z" }, + { url = "https://files.pythonhosted.org/packages/a1/37/cd45f1f051fe6230f751cc5cdd2728bb3a203f5619510ef11e732109593c/contourpy-1.3.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:745b57db7758f3ffc05a10254edd3182a2a83402a89c00957a8e8a22f5582823", size = 310443, upload-time = "2025-04-15T17:43:44.522Z" }, + { url = "https://files.pythonhosted.org/packages/8b/a2/36ea6140c306c9ff6dd38e3bcec80b3b018474ef4d17eb68ceecd26675f4/contourpy-1.3.2-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:970e9173dbd7eba9b4e01aab19215a48ee5dd3f43cef736eebde064a171f89a5", size = 349865, upload-time = "2025-04-15T17:43:49.545Z" }, + { url = "https://files.pythonhosted.org/packages/95/b7/2fc76bc539693180488f7b6cc518da7acbbb9e3b931fd9280504128bf956/contourpy-1.3.2-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c6c4639a9c22230276b7bffb6a850dfc8258a2521305e1faefe804d006b2e532", size = 321162, upload-time = "2025-04-15T17:43:54.203Z" }, + { url = "https://files.pythonhosted.org/packages/f4/10/76d4f778458b0aa83f96e59d65ece72a060bacb20cfbee46cf6cd5ceba41/contourpy-1.3.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc829960f34ba36aad4302e78eabf3ef16a3a100863f0d4eeddf30e8a485a03b", size = 327355, upload-time = "2025-04-15T17:44:01.025Z" }, + { url = "https://files.pythonhosted.org/packages/43/a3/10cf483ea683f9f8ab096c24bad3cce20e0d1dd9a4baa0e2093c1c962d9d/contourpy-1.3.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d32530b534e986374fc19eaa77fcb87e8a99e5431499949b828312bdcd20ac52", size = 1307935, upload-time = "2025-04-15T17:44:17.322Z" }, + { url = "https://files.pythonhosted.org/packages/78/73/69dd9a024444489e22d86108e7b913f3528f56cfc312b5c5727a44188471/contourpy-1.3.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e298e7e70cf4eb179cc1077be1c725b5fd131ebc81181bf0c03525c8abc297fd", size = 1372168, upload-time = "2025-04-15T17:44:33.43Z" }, + { url = "https://files.pythonhosted.org/packages/0f/1b/96d586ccf1b1a9d2004dd519b25fbf104a11589abfd05484ff12199cca21/contourpy-1.3.2-cp313-cp313t-win32.whl", hash = "sha256:d0e589ae0d55204991450bb5c23f571c64fe43adaa53f93fc902a84c96f52fe1", size = 189550, upload-time = "2025-04-15T17:44:37.092Z" }, + { url = "https://files.pythonhosted.org/packages/b0/e6/6000d0094e8a5e32ad62591c8609e269febb6e4db83a1c75ff8868b42731/contourpy-1.3.2-cp313-cp313t-win_amd64.whl", hash = "sha256:78e9253c3de756b3f6a5174d024c4835acd59eb3f8e2ca13e775dbffe1558f69", size = 238214, upload-time = "2025-04-15T17:44:40.827Z" }, { url = "https://files.pythonhosted.org/packages/33/05/b26e3c6ecc05f349ee0013f0bb850a761016d89cec528a98193a48c34033/contourpy-1.3.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:fd93cc7f3139b6dd7aab2f26a90dde0aa9fc264dbf70f6740d498a70b860b82c", size = 265681, upload-time = "2025-04-15T17:44:59.314Z" }, { url = "https://files.pythonhosted.org/packages/2b/25/ac07d6ad12affa7d1ffed11b77417d0a6308170f44ff20fa1d5aa6333f03/contourpy-1.3.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:107ba8a6a7eec58bb475329e6d3b95deba9440667c4d62b9b6063942b61d7f16", size = 315101, upload-time = "2025-04-15T17:45:04.165Z" }, { url = "https://files.pythonhosted.org/packages/8f/4d/5bb3192bbe9d3f27e3061a6a8e7733c9120e203cb8515767d30973f71030/contourpy-1.3.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ded1706ed0c1049224531b81128efbd5084598f18d8a2d9efae833edbd2b40ad", size = 220599, upload-time = "2025-04-15T17:45:08.456Z" }, @@ -168,20 +201,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", size = 16674, upload-time = "2025-05-10T17:42:49.33Z" }, ] -[[package]] -name = "flake8" -version = "7.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mccabe" }, - { name = "pycodestyle" }, - { name = "pyflakes" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e7/c4/5842fc9fc94584c455543540af62fd9900faade32511fab650e9891ec225/flake8-7.2.0.tar.gz", hash = "sha256:fa558ae3f6f7dbf2b4f22663e5343b6b6023620461f8d4ff2019ef4b5ee70426", size = 48177, upload-time = "2025-03-29T20:08:39.329Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/83/5c/0627be4c9976d56b1217cb5187b7504e7fd7d3503f8bfd312a04077bd4f7/flake8-7.2.0-py2.py3-none-any.whl", hash = "sha256:93b92ba5bdb60754a6da14fa3b93a9361fd00a59632ada61fd7b130436c40343", size = 57786, upload-time = "2025-03-29T20:08:37.902Z" }, -] - [[package]] name = "fonttools" version = "4.58.4" @@ -212,6 +231,14 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ae/44/a3a3b70d5709405f7525bb7cb497b4e46151e0c02e3c8a0e40e5e9fe030b/fonttools-4.58.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e38f687d5de97c7fb7da3e58169fb5ba349e464e141f83c3c2e2beb91d317816", size = 5037851, upload-time = "2025-06-13T17:24:35.034Z" }, { url = "https://files.pythonhosted.org/packages/21/cb/e8923d197c78969454eb876a4a55a07b59c9c4c46598f02b02411dc3b45c/fonttools-4.58.4-cp312-cp312-win32.whl", hash = "sha256:636c073b4da9db053aa683db99580cac0f7c213a953b678f69acbca3443c12cc", size = 2187428, upload-time = "2025-06-13T17:24:36.996Z" }, { url = "https://files.pythonhosted.org/packages/46/e6/fe50183b1a0e1018e7487ee740fa8bb127b9f5075a41e20d017201e8ab14/fonttools-4.58.4-cp312-cp312-win_amd64.whl", hash = "sha256:82e8470535743409b30913ba2822e20077acf9ea70acec40b10fcf5671dceb58", size = 2236649, upload-time = "2025-06-13T17:24:38.985Z" }, + { url = "https://files.pythonhosted.org/packages/d4/4f/c05cab5fc1a4293e6bc535c6cb272607155a0517700f5418a4165b7f9ec8/fonttools-4.58.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:5f4a64846495c543796fa59b90b7a7a9dff6839bd852741ab35a71994d685c6d", size = 2745197, upload-time = "2025-06-13T17:24:40.645Z" }, + { url = "https://files.pythonhosted.org/packages/3e/d3/49211b1f96ae49308f4f78ca7664742377a6867f00f704cdb31b57e4b432/fonttools-4.58.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e80661793a5d4d7ad132a2aa1eae2e160fbdbb50831a0edf37c7c63b2ed36574", size = 2317272, upload-time = "2025-06-13T17:24:43.428Z" }, + { url = "https://files.pythonhosted.org/packages/b2/11/c9972e46a6abd752a40a46960e431c795ad1f306775fc1f9e8c3081a1274/fonttools-4.58.4-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fe5807fc64e4ba5130f1974c045a6e8d795f3b7fb6debfa511d1773290dbb76b", size = 4877184, upload-time = "2025-06-13T17:24:45.527Z" }, + { url = "https://files.pythonhosted.org/packages/ea/24/5017c01c9ef8df572cc9eaf9f12be83ad8ed722ff6dc67991d3d752956e4/fonttools-4.58.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b610b9bef841cb8f4b50472494158b1e347d15cad56eac414c722eda695a6cfd", size = 4939445, upload-time = "2025-06-13T17:24:47.647Z" }, + { url = "https://files.pythonhosted.org/packages/79/b0/538cc4d0284b5a8826b4abed93a69db52e358525d4b55c47c8cef3669767/fonttools-4.58.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2daa7f0e213c38f05f054eb5e1730bd0424aebddbeac094489ea1585807dd187", size = 4878800, upload-time = "2025-06-13T17:24:49.766Z" }, + { url = "https://files.pythonhosted.org/packages/5a/9b/a891446b7a8250e65bffceb248508587958a94db467ffd33972723ab86c9/fonttools-4.58.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:66cccb6c0b944496b7f26450e9a66e997739c513ffaac728d24930df2fd9d35b", size = 5021259, upload-time = "2025-06-13T17:24:51.754Z" }, + { url = "https://files.pythonhosted.org/packages/17/b2/c4d2872cff3ace3ddd1388bf15b76a1d8d5313f0a61f234e9aed287e674d/fonttools-4.58.4-cp313-cp313-win32.whl", hash = "sha256:94d2aebb5ca59a5107825520fde596e344652c1f18170ef01dacbe48fa60c889", size = 2185824, upload-time = "2025-06-13T17:24:54.324Z" }, + { url = "https://files.pythonhosted.org/packages/98/57/cddf8bcc911d4f47dfca1956c1e3aeeb9f7c9b8e88b2a312fe8c22714e0b/fonttools-4.58.4-cp313-cp313-win_amd64.whl", hash = "sha256:b554bd6e80bba582fd326ddab296e563c20c64dca816d5e30489760e0c41529f", size = 2236382, upload-time = "2025-06-13T17:24:56.291Z" }, { url = "https://files.pythonhosted.org/packages/0b/2f/c536b5b9bb3c071e91d536a4d11f969e911dbb6b227939f4c5b0bca090df/fonttools-4.58.4-py3-none-any.whl", hash = "sha256:a10ce13a13f26cbb9f37512a4346bb437ad7e002ff6fa966a7ce7ff5ac3528bd", size = 1114660, upload-time = "2025-06-13T17:25:13.321Z" }, ] @@ -316,6 +343,34 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/32/d8/014b89fee5d4dce157d814303b0fce4d31385a2af4c41fed194b173b81ac/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:085940635c62697391baafaaeabdf3dd7a6c3643577dde337f4d66eba021b2b8", size = 2338410, upload-time = "2024-12-24T18:29:39.991Z" }, { url = "https://files.pythonhosted.org/packages/bd/72/dfff0cc97f2a0776e1c9eb5bef1ddfd45f46246c6533b0191887a427bca5/kiwisolver-1.4.8-cp312-cp312-win_amd64.whl", hash = "sha256:01c3d31902c7db5fb6182832713d3b4122ad9317c2c5877d0539227d96bb2e50", size = 71853, upload-time = "2024-12-24T18:29:42.006Z" }, { url = "https://files.pythonhosted.org/packages/dc/85/220d13d914485c0948a00f0b9eb419efaf6da81b7d72e88ce2391f7aed8d/kiwisolver-1.4.8-cp312-cp312-win_arm64.whl", hash = "sha256:a3c44cb68861de93f0c4a8175fbaa691f0aa22550c331fefef02b618a9dcb476", size = 65424, upload-time = "2024-12-24T18:29:44.38Z" }, + { url = "https://files.pythonhosted.org/packages/79/b3/e62464a652f4f8cd9006e13d07abad844a47df1e6537f73ddfbf1bc997ec/kiwisolver-1.4.8-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:1c8ceb754339793c24aee1c9fb2485b5b1f5bb1c2c214ff13368431e51fc9a09", size = 124156, upload-time = "2024-12-24T18:29:45.368Z" }, + { url = "https://files.pythonhosted.org/packages/8d/2d/f13d06998b546a2ad4f48607a146e045bbe48030774de29f90bdc573df15/kiwisolver-1.4.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:54a62808ac74b5e55a04a408cda6156f986cefbcf0ada13572696b507cc92fa1", size = 66555, upload-time = "2024-12-24T18:29:46.37Z" }, + { url = "https://files.pythonhosted.org/packages/59/e3/b8bd14b0a54998a9fd1e8da591c60998dc003618cb19a3f94cb233ec1511/kiwisolver-1.4.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:68269e60ee4929893aad82666821aaacbd455284124817af45c11e50a4b42e3c", size = 65071, upload-time = "2024-12-24T18:29:47.333Z" }, + { url = "https://files.pythonhosted.org/packages/f0/1c/6c86f6d85ffe4d0ce04228d976f00674f1df5dc893bf2dd4f1928748f187/kiwisolver-1.4.8-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:34d142fba9c464bc3bbfeff15c96eab0e7310343d6aefb62a79d51421fcc5f1b", size = 1378053, upload-time = "2024-12-24T18:29:49.636Z" }, + { url = "https://files.pythonhosted.org/packages/4e/b9/1c6e9f6dcb103ac5cf87cb695845f5fa71379021500153566d8a8a9fc291/kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc373e0eef45b59197de815b1b28ef89ae3955e7722cc9710fb91cd77b7f47", size = 1472278, upload-time = "2024-12-24T18:29:51.164Z" }, + { url = "https://files.pythonhosted.org/packages/ee/81/aca1eb176de671f8bda479b11acdc42c132b61a2ac861c883907dde6debb/kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:77e6f57a20b9bd4e1e2cedda4d0b986ebd0216236f0106e55c28aea3d3d69b16", size = 1478139, upload-time = "2024-12-24T18:29:52.594Z" }, + { url = "https://files.pythonhosted.org/packages/49/f4/e081522473671c97b2687d380e9e4c26f748a86363ce5af48b4a28e48d06/kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08e77738ed7538f036cd1170cbed942ef749137b1311fa2bbe2a7fda2f6bf3cc", size = 1413517, upload-time = "2024-12-24T18:29:53.941Z" }, + { url = "https://files.pythonhosted.org/packages/8f/e9/6a7d025d8da8c4931522922cd706105aa32b3291d1add8c5427cdcd66e63/kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5ce1e481a74b44dd5e92ff03ea0cb371ae7a0268318e202be06c8f04f4f1246", size = 1474952, upload-time = "2024-12-24T18:29:56.523Z" }, + { url = "https://files.pythonhosted.org/packages/82/13/13fa685ae167bee5d94b415991c4fc7bb0a1b6ebea6e753a87044b209678/kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:fc2ace710ba7c1dfd1a3b42530b62b9ceed115f19a1656adefce7b1782a37794", size = 2269132, upload-time = "2024-12-24T18:29:57.989Z" }, + { url = "https://files.pythonhosted.org/packages/ef/92/bb7c9395489b99a6cb41d502d3686bac692586db2045adc19e45ee64ed23/kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:3452046c37c7692bd52b0e752b87954ef86ee2224e624ef7ce6cb21e8c41cc1b", size = 2425997, upload-time = "2024-12-24T18:29:59.393Z" }, + { url = "https://files.pythonhosted.org/packages/ed/12/87f0e9271e2b63d35d0d8524954145837dd1a6c15b62a2d8c1ebe0f182b4/kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7e9a60b50fe8b2ec6f448fe8d81b07e40141bfced7f896309df271a0b92f80f3", size = 2376060, upload-time = "2024-12-24T18:30:01.338Z" }, + { url = "https://files.pythonhosted.org/packages/02/6e/c8af39288edbce8bf0fa35dee427b082758a4b71e9c91ef18fa667782138/kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:918139571133f366e8362fa4a297aeba86c7816b7ecf0bc79168080e2bd79957", size = 2520471, upload-time = "2024-12-24T18:30:04.574Z" }, + { url = "https://files.pythonhosted.org/packages/13/78/df381bc7b26e535c91469f77f16adcd073beb3e2dd25042efd064af82323/kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e063ef9f89885a1d68dd8b2e18f5ead48653176d10a0e324e3b0030e3a69adeb", size = 2338793, upload-time = "2024-12-24T18:30:06.25Z" }, + { url = "https://files.pythonhosted.org/packages/d0/dc/c1abe38c37c071d0fc71c9a474fd0b9ede05d42f5a458d584619cfd2371a/kiwisolver-1.4.8-cp313-cp313-win_amd64.whl", hash = "sha256:a17b7c4f5b2c51bb68ed379defd608a03954a1845dfed7cc0117f1cc8a9b7fd2", size = 71855, upload-time = "2024-12-24T18:30:07.535Z" }, + { url = "https://files.pythonhosted.org/packages/a0/b6/21529d595b126ac298fdd90b705d87d4c5693de60023e0efcb4f387ed99e/kiwisolver-1.4.8-cp313-cp313-win_arm64.whl", hash = "sha256:3cd3bc628b25f74aedc6d374d5babf0166a92ff1317f46267f12d2ed54bc1d30", size = 65430, upload-time = "2024-12-24T18:30:08.504Z" }, + { url = "https://files.pythonhosted.org/packages/34/bd/b89380b7298e3af9b39f49334e3e2a4af0e04819789f04b43d560516c0c8/kiwisolver-1.4.8-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:370fd2df41660ed4e26b8c9d6bbcad668fbe2560462cba151a721d49e5b6628c", size = 126294, upload-time = "2024-12-24T18:30:09.508Z" }, + { url = "https://files.pythonhosted.org/packages/83/41/5857dc72e5e4148eaac5aa76e0703e594e4465f8ab7ec0fc60e3a9bb8fea/kiwisolver-1.4.8-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:84a2f830d42707de1d191b9490ac186bf7997a9495d4e9072210a1296345f7dc", size = 67736, upload-time = "2024-12-24T18:30:11.039Z" }, + { url = "https://files.pythonhosted.org/packages/e1/d1/be059b8db56ac270489fb0b3297fd1e53d195ba76e9bbb30e5401fa6b759/kiwisolver-1.4.8-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:7a3ad337add5148cf51ce0b55642dc551c0b9d6248458a757f98796ca7348712", size = 66194, upload-time = "2024-12-24T18:30:14.886Z" }, + { url = "https://files.pythonhosted.org/packages/e1/83/4b73975f149819eb7dcf9299ed467eba068ecb16439a98990dcb12e63fdd/kiwisolver-1.4.8-cp313-cp313t-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7506488470f41169b86d8c9aeff587293f530a23a23a49d6bc64dab66bedc71e", size = 1465942, upload-time = "2024-12-24T18:30:18.927Z" }, + { url = "https://files.pythonhosted.org/packages/c7/2c/30a5cdde5102958e602c07466bce058b9d7cb48734aa7a4327261ac8e002/kiwisolver-1.4.8-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f0121b07b356a22fb0414cec4666bbe36fd6d0d759db3d37228f496ed67c880", size = 1595341, upload-time = "2024-12-24T18:30:22.102Z" }, + { url = "https://files.pythonhosted.org/packages/ff/9b/1e71db1c000385aa069704f5990574b8244cce854ecd83119c19e83c9586/kiwisolver-1.4.8-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d6d6bd87df62c27d4185de7c511c6248040afae67028a8a22012b010bc7ad062", size = 1598455, upload-time = "2024-12-24T18:30:24.947Z" }, + { url = "https://files.pythonhosted.org/packages/85/92/c8fec52ddf06231b31cbb779af77e99b8253cd96bd135250b9498144c78b/kiwisolver-1.4.8-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:291331973c64bb9cce50bbe871fb2e675c4331dab4f31abe89f175ad7679a4d7", size = 1522138, upload-time = "2024-12-24T18:30:26.286Z" }, + { url = "https://files.pythonhosted.org/packages/0b/51/9eb7e2cd07a15d8bdd976f6190c0164f92ce1904e5c0c79198c4972926b7/kiwisolver-1.4.8-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:893f5525bb92d3d735878ec00f781b2de998333659507d29ea4466208df37bed", size = 1582857, upload-time = "2024-12-24T18:30:28.86Z" }, + { url = "https://files.pythonhosted.org/packages/0f/95/c5a00387a5405e68ba32cc64af65ce881a39b98d73cc394b24143bebc5b8/kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b47a465040146981dc9db8647981b8cb96366fbc8d452b031e4f8fdffec3f26d", size = 2293129, upload-time = "2024-12-24T18:30:30.34Z" }, + { url = "https://files.pythonhosted.org/packages/44/83/eeb7af7d706b8347548313fa3a3a15931f404533cc54fe01f39e830dd231/kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:99cea8b9dd34ff80c521aef46a1dddb0dcc0283cf18bde6d756f1e6f31772165", size = 2421538, upload-time = "2024-12-24T18:30:33.334Z" }, + { url = "https://files.pythonhosted.org/packages/05/f9/27e94c1b3eb29e6933b6986ffc5fa1177d2cd1f0c8efc5f02c91c9ac61de/kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:151dffc4865e5fe6dafce5480fab84f950d14566c480c08a53c663a0020504b6", size = 2390661, upload-time = "2024-12-24T18:30:34.939Z" }, + { url = "https://files.pythonhosted.org/packages/d9/d4/3c9735faa36ac591a4afcc2980d2691000506050b7a7e80bcfe44048daa7/kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:577facaa411c10421314598b50413aa1ebcf5126f704f1e5d72d7e4e9f020d90", size = 2546710, upload-time = "2024-12-24T18:30:37.281Z" }, + { url = "https://files.pythonhosted.org/packages/4c/fa/be89a49c640930180657482a74970cdcf6f7072c8d2471e1babe17a222dc/kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:be4816dc51c8a471749d664161b434912eee82f2ea66bd7628bd14583a833e85", size = 2349213, upload-time = "2024-12-24T18:30:40.019Z" }, { url = "https://files.pythonhosted.org/packages/1f/f9/ae81c47a43e33b93b0a9819cac6723257f5da2a5a60daf46aa5c7226ea85/kiwisolver-1.4.8-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e7a019419b7b510f0f7c9dceff8c5eae2392037eae483a7f9162625233802b0a", size = 60403, upload-time = "2024-12-24T18:30:41.372Z" }, { url = "https://files.pythonhosted.org/packages/58/ca/f92b5cb6f4ce0c1ebfcfe3e2e42b96917e16f7090e45b21102941924f18f/kiwisolver-1.4.8-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:286b18e86682fd2217a48fc6be6b0f20c1d0ed10958d8dc53453ad58d7be0bf8", size = 58657, upload-time = "2024-12-24T18:30:42.392Z" }, { url = "https://files.pythonhosted.org/packages/80/28/ae0240f732f0484d3a4dc885d055653c47144bdf59b670aae0ec3c65a7c8/kiwisolver-1.4.8-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4191ee8dfd0be1c3666ccbac178c5a05d5f8d689bbe3fc92f3c4abec817f8fe0", size = 84948, upload-time = "2024-12-24T18:30:44.703Z" }, @@ -360,6 +415,26 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352, upload-time = "2024-10-18T15:21:20.971Z" }, { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097, upload-time = "2024-10-18T15:21:22.646Z" }, { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601, upload-time = "2024-10-18T15:21:23.499Z" }, + { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274, upload-time = "2024-10-18T15:21:24.577Z" }, + { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352, upload-time = "2024-10-18T15:21:25.382Z" }, + { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122, upload-time = "2024-10-18T15:21:26.199Z" }, + { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085, upload-time = "2024-10-18T15:21:27.029Z" }, + { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978, upload-time = "2024-10-18T15:21:27.846Z" }, + { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208, upload-time = "2024-10-18T15:21:28.744Z" }, + { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357, upload-time = "2024-10-18T15:21:29.545Z" }, + { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344, upload-time = "2024-10-18T15:21:30.366Z" }, + { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101, upload-time = "2024-10-18T15:21:31.207Z" }, + { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603, upload-time = "2024-10-18T15:21:32.032Z" }, + { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510, upload-time = "2024-10-18T15:21:33.625Z" }, + { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486, upload-time = "2024-10-18T15:21:34.611Z" }, + { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480, upload-time = "2024-10-18T15:21:35.398Z" }, + { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914, upload-time = "2024-10-18T15:21:36.231Z" }, + { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796, upload-time = "2024-10-18T15:21:37.073Z" }, + { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473, upload-time = "2024-10-18T15:21:37.932Z" }, + { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114, upload-time = "2024-10-18T15:21:39.799Z" }, + { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098, upload-time = "2024-10-18T15:21:40.813Z" }, + { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208, upload-time = "2024-10-18T15:21:41.814Z" }, + { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739, upload-time = "2024-10-18T15:21:42.784Z" }, ] [[package]] @@ -398,20 +473,23 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c4/91/ba0ae1ff4b3f30972ad01cd4a8029e70a0ec3b8ea5be04764b128b66f763/matplotlib-3.10.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed70453fd99733293ace1aec568255bc51c6361cb0da94fa5ebf0649fdb2150a", size = 8601321, upload-time = "2025-05-08T19:10:14.47Z" }, { url = "https://files.pythonhosted.org/packages/d2/88/d636041eb54a84b889e11872d91f7cbf036b3b0e194a70fa064eb8b04f7a/matplotlib-3.10.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dbed9917b44070e55640bd13419de83b4c918e52d97561544814ba463811cbc7", size = 9406972, upload-time = "2025-05-08T19:10:16.569Z" }, { url = "https://files.pythonhosted.org/packages/b1/79/0d1c165eac44405a86478082e225fce87874f7198300bbebc55faaf6d28d/matplotlib-3.10.3-cp312-cp312-win_amd64.whl", hash = "sha256:cf37d8c6ef1a48829443e8ba5227b44236d7fcaf7647caa3178a4ff9f7a5be05", size = 8067954, upload-time = "2025-05-08T19:10:18.663Z" }, + { url = "https://files.pythonhosted.org/packages/3b/c1/23cfb566a74c696a3b338d8955c549900d18fe2b898b6e94d682ca21e7c2/matplotlib-3.10.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9f2efccc8dcf2b86fc4ee849eea5dcaecedd0773b30f47980dc0cbeabf26ec84", size = 8180318, upload-time = "2025-05-08T19:10:20.426Z" }, + { url = "https://files.pythonhosted.org/packages/6c/0c/02f1c3b66b30da9ee343c343acbb6251bef5b01d34fad732446eaadcd108/matplotlib-3.10.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3ddbba06a6c126e3301c3d272a99dcbe7f6c24c14024e80307ff03791a5f294e", size = 8051132, upload-time = "2025-05-08T19:10:22.569Z" }, + { url = "https://files.pythonhosted.org/packages/b4/ab/8db1a5ac9b3a7352fb914133001dae889f9fcecb3146541be46bed41339c/matplotlib-3.10.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:748302b33ae9326995b238f606e9ed840bf5886ebafcb233775d946aa8107a15", size = 8457633, upload-time = "2025-05-08T19:10:24.749Z" }, + { url = "https://files.pythonhosted.org/packages/f5/64/41c4367bcaecbc03ef0d2a3ecee58a7065d0a36ae1aa817fe573a2da66d4/matplotlib-3.10.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a80fcccbef63302c0efd78042ea3c2436104c5b1a4d3ae20f864593696364ac7", size = 8601031, upload-time = "2025-05-08T19:10:27.03Z" }, + { url = "https://files.pythonhosted.org/packages/12/6f/6cc79e9e5ab89d13ed64da28898e40fe5b105a9ab9c98f83abd24e46d7d7/matplotlib-3.10.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:55e46cbfe1f8586adb34f7587c3e4f7dedc59d5226719faf6cb54fc24f2fd52d", size = 9406988, upload-time = "2025-05-08T19:10:29.056Z" }, + { url = "https://files.pythonhosted.org/packages/b1/0f/eed564407bd4d935ffabf561ed31099ed609e19287409a27b6d336848653/matplotlib-3.10.3-cp313-cp313-win_amd64.whl", hash = "sha256:151d89cb8d33cb23345cd12490c76fd5d18a56581a16d950b48c6ff19bb2ab93", size = 8068034, upload-time = "2025-05-08T19:10:31.221Z" }, + { url = "https://files.pythonhosted.org/packages/3e/e5/2f14791ff69b12b09e9975e1d116d9578ac684460860ce542c2588cb7a1c/matplotlib-3.10.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:c26dd9834e74d164d06433dc7be5d75a1e9890b926b3e57e74fa446e1a62c3e2", size = 8218223, upload-time = "2025-05-08T19:10:33.114Z" }, + { url = "https://files.pythonhosted.org/packages/5c/08/30a94afd828b6e02d0a52cae4a29d6e9ccfcf4c8b56cc28b021d3588873e/matplotlib-3.10.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:24853dad5b8c84c8c2390fc31ce4858b6df504156893292ce8092d190ef8151d", size = 8094985, upload-time = "2025-05-08T19:10:35.337Z" }, + { url = "https://files.pythonhosted.org/packages/89/44/f3bc6b53066c889d7a1a3ea8094c13af6a667c5ca6220ec60ecceec2dabe/matplotlib-3.10.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68f7878214d369d7d4215e2a9075fef743be38fa401d32e6020bab2dfabaa566", size = 8483109, upload-time = "2025-05-08T19:10:37.611Z" }, + { url = "https://files.pythonhosted.org/packages/ba/c7/473bc559beec08ebee9f86ca77a844b65747e1a6c2691e8c92e40b9f42a8/matplotlib-3.10.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6929fc618cb6db9cb75086f73b3219bbb25920cb24cee2ea7a12b04971a4158", size = 8618082, upload-time = "2025-05-08T19:10:39.892Z" }, + { url = "https://files.pythonhosted.org/packages/d8/e9/6ce8edd264c8819e37bbed8172e0ccdc7107fe86999b76ab5752276357a4/matplotlib-3.10.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6c7818292a5cc372a2dc4c795e5c356942eb8350b98ef913f7fda51fe175ac5d", size = 9413699, upload-time = "2025-05-08T19:10:42.376Z" }, + { url = "https://files.pythonhosted.org/packages/1b/92/9a45c91089c3cf690b5badd4be81e392ff086ccca8a1d4e3a08463d8a966/matplotlib-3.10.3-cp313-cp313t-win_amd64.whl", hash = "sha256:4f23ffe95c5667ef8a2b56eea9b53db7f43910fa4a2d5472ae0f72b64deab4d5", size = 8139044, upload-time = "2025-05-08T19:10:44.551Z" }, { url = "https://files.pythonhosted.org/packages/3d/d1/f54d43e95384b312ffa4a74a4326c722f3b8187aaaa12e9a84cdf3037131/matplotlib-3.10.3-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:86ab63d66bbc83fdb6733471d3bff40897c1e9921cba112accd748eee4bce5e4", size = 8162896, upload-time = "2025-05-08T19:10:46.432Z" }, { url = "https://files.pythonhosted.org/packages/24/a4/fbfc00c2346177c95b353dcf9b5a004106abe8730a62cb6f27e79df0a698/matplotlib-3.10.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:a48f9c08bf7444b5d2391a83e75edb464ccda3c380384b36532a0962593a1751", size = 8039702, upload-time = "2025-05-08T19:10:49.634Z" }, { url = "https://files.pythonhosted.org/packages/6a/b9/59e120d24a2ec5fc2d30646adb2efb4621aab3c6d83d66fb2a7a182db032/matplotlib-3.10.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb73d8aa75a237457988f9765e4dfe1c0d2453c5ca4eabc897d4309672c8e014", size = 8594298, upload-time = "2025-05-08T19:10:51.738Z" }, ] -[[package]] -name = "mccabe" -version = "0.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/ff/0ffefdcac38932a54d2b5eed4e0ba8a408f215002cd178ad1df0f2806ff8/mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", size = 9658, upload-time = "2022-01-24T01:14:51.113Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/27/1a/1f68f9ba0c207934b35b86a8ca3aad8395a3d6dd7921c0686e23853ff5a9/mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e", size = 7350, upload-time = "2022-01-24T01:14:49.62Z" }, -] - [[package]] name = "numpy" version = "2.2.6" @@ -451,6 +529,26 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b7/25/5761d832a81df431e260719ec45de696414266613c9ee268394dd5ad8236/numpy-2.2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de", size = 18313783, upload-time = "2025-05-17T21:36:56.883Z" }, { url = "https://files.pythonhosted.org/packages/57/0a/72d5a3527c5ebffcd47bde9162c39fae1f90138c961e5296491ce778e682/numpy-2.2.6-cp312-cp312-win32.whl", hash = "sha256:4eeaae00d789f66c7a25ac5f34b71a7035bb474e679f410e5e1a94deb24cf2d4", size = 6246506, upload-time = "2025-05-17T21:37:07.368Z" }, { url = "https://files.pythonhosted.org/packages/36/fa/8c9210162ca1b88529ab76b41ba02d433fd54fecaf6feb70ef9f124683f1/numpy-2.2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c1f9540be57940698ed329904db803cf7a402f3fc200bfe599334c9bd84a40b2", size = 12614190, upload-time = "2025-05-17T21:37:26.213Z" }, + { url = "https://files.pythonhosted.org/packages/f9/5c/6657823f4f594f72b5471f1db1ab12e26e890bb2e41897522d134d2a3e81/numpy-2.2.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0811bb762109d9708cca4d0b13c4f67146e3c3b7cf8d34018c722adb2d957c84", size = 20867828, upload-time = "2025-05-17T21:37:56.699Z" }, + { url = "https://files.pythonhosted.org/packages/dc/9e/14520dc3dadf3c803473bd07e9b2bd1b69bc583cb2497b47000fed2fa92f/numpy-2.2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:287cc3162b6f01463ccd86be154f284d0893d2b3ed7292439ea97eafa8170e0b", size = 14143006, upload-time = "2025-05-17T21:38:18.291Z" }, + { url = "https://files.pythonhosted.org/packages/4f/06/7e96c57d90bebdce9918412087fc22ca9851cceaf5567a45c1f404480e9e/numpy-2.2.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f1372f041402e37e5e633e586f62aa53de2eac8d98cbfb822806ce4bbefcb74d", size = 5076765, upload-time = "2025-05-17T21:38:27.319Z" }, + { url = "https://files.pythonhosted.org/packages/73/ed/63d920c23b4289fdac96ddbdd6132e9427790977d5457cd132f18e76eae0/numpy-2.2.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:55a4d33fa519660d69614a9fad433be87e5252f4b03850642f88993f7b2ca566", size = 6617736, upload-time = "2025-05-17T21:38:38.141Z" }, + { url = "https://files.pythonhosted.org/packages/85/c5/e19c8f99d83fd377ec8c7e0cf627a8049746da54afc24ef0a0cb73d5dfb5/numpy-2.2.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f92729c95468a2f4f15e9bb94c432a9229d0d50de67304399627a943201baa2f", size = 14010719, upload-time = "2025-05-17T21:38:58.433Z" }, + { url = "https://files.pythonhosted.org/packages/19/49/4df9123aafa7b539317bf6d342cb6d227e49f7a35b99c287a6109b13dd93/numpy-2.2.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bc23a79bfabc5d056d106f9befb8d50c31ced2fbc70eedb8155aec74a45798f", size = 16526072, upload-time = "2025-05-17T21:39:22.638Z" }, + { url = "https://files.pythonhosted.org/packages/b2/6c/04b5f47f4f32f7c2b0e7260442a8cbcf8168b0e1a41ff1495da42f42a14f/numpy-2.2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3143e4451880bed956e706a3220b4e5cf6172ef05fcc397f6f36a550b1dd868", size = 15503213, upload-time = "2025-05-17T21:39:45.865Z" }, + { url = "https://files.pythonhosted.org/packages/17/0a/5cd92e352c1307640d5b6fec1b2ffb06cd0dabe7d7b8227f97933d378422/numpy-2.2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4f13750ce79751586ae2eb824ba7e1e8dba64784086c98cdbbcc6a42112ce0d", size = 18316632, upload-time = "2025-05-17T21:40:13.331Z" }, + { url = "https://files.pythonhosted.org/packages/f0/3b/5cba2b1d88760ef86596ad0f3d484b1cbff7c115ae2429678465057c5155/numpy-2.2.6-cp313-cp313-win32.whl", hash = "sha256:5beb72339d9d4fa36522fc63802f469b13cdbe4fdab4a288f0c441b74272ebfd", size = 6244532, upload-time = "2025-05-17T21:43:46.099Z" }, + { url = "https://files.pythonhosted.org/packages/cb/3b/d58c12eafcb298d4e6d0d40216866ab15f59e55d148a5658bb3132311fcf/numpy-2.2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b0544343a702fa80c95ad5d3d608ea3599dd54d4632df855e4c8d24eb6ecfa1c", size = 12610885, upload-time = "2025-05-17T21:44:05.145Z" }, + { url = "https://files.pythonhosted.org/packages/6b/9e/4bf918b818e516322db999ac25d00c75788ddfd2d2ade4fa66f1f38097e1/numpy-2.2.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0bca768cd85ae743b2affdc762d617eddf3bcf8724435498a1e80132d04879e6", size = 20963467, upload-time = "2025-05-17T21:40:44Z" }, + { url = "https://files.pythonhosted.org/packages/61/66/d2de6b291507517ff2e438e13ff7b1e2cdbdb7cb40b3ed475377aece69f9/numpy-2.2.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fc0c5673685c508a142ca65209b4e79ed6740a4ed6b2267dbba90f34b0b3cfda", size = 14225144, upload-time = "2025-05-17T21:41:05.695Z" }, + { url = "https://files.pythonhosted.org/packages/e4/25/480387655407ead912e28ba3a820bc69af9adf13bcbe40b299d454ec011f/numpy-2.2.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:5bd4fc3ac8926b3819797a7c0e2631eb889b4118a9898c84f585a54d475b7e40", size = 5200217, upload-time = "2025-05-17T21:41:15.903Z" }, + { url = "https://files.pythonhosted.org/packages/aa/4a/6e313b5108f53dcbf3aca0c0f3e9c92f4c10ce57a0a721851f9785872895/numpy-2.2.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:fee4236c876c4e8369388054d02d0e9bb84821feb1a64dd59e137e6511a551f8", size = 6712014, upload-time = "2025-05-17T21:41:27.321Z" }, + { url = "https://files.pythonhosted.org/packages/b7/30/172c2d5c4be71fdf476e9de553443cf8e25feddbe185e0bd88b096915bcc/numpy-2.2.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1dda9c7e08dc141e0247a5b8f49cf05984955246a327d4c48bda16821947b2f", size = 14077935, upload-time = "2025-05-17T21:41:49.738Z" }, + { url = "https://files.pythonhosted.org/packages/12/fb/9e743f8d4e4d3c710902cf87af3512082ae3d43b945d5d16563f26ec251d/numpy-2.2.6-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f447e6acb680fd307f40d3da4852208af94afdfab89cf850986c3ca00562f4fa", size = 16600122, upload-time = "2025-05-17T21:42:14.046Z" }, + { url = "https://files.pythonhosted.org/packages/12/75/ee20da0e58d3a66f204f38916757e01e33a9737d0b22373b3eb5a27358f9/numpy-2.2.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:389d771b1623ec92636b0786bc4ae56abafad4a4c513d36a55dce14bd9ce8571", size = 15586143, upload-time = "2025-05-17T21:42:37.464Z" }, + { url = "https://files.pythonhosted.org/packages/76/95/bef5b37f29fc5e739947e9ce5179ad402875633308504a52d188302319c8/numpy-2.2.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8e9ace4a37db23421249ed236fdcdd457d671e25146786dfc96835cd951aa7c1", size = 18385260, upload-time = "2025-05-17T21:43:05.189Z" }, + { url = "https://files.pythonhosted.org/packages/09/04/f2f83279d287407cf36a7a8053a5abe7be3622a4363337338f2585e4afda/numpy-2.2.6-cp313-cp313t-win32.whl", hash = "sha256:038613e9fb8c72b0a41f025a7e4c3f0b7a1b5d768ece4796b674c8f3fe13efff", size = 6377225, upload-time = "2025-05-17T21:43:16.254Z" }, + { url = "https://files.pythonhosted.org/packages/67/0e/35082d13c09c02c011cf21570543d202ad929d961c02a147493cb0c2bdf5/numpy-2.2.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6031dd6dfecc0cf9f668681a37648373bddd6421fff6c66ec1624eed0180ee06", size = 12771374, upload-time = "2025-05-17T21:43:35.479Z" }, { url = "https://files.pythonhosted.org/packages/9e/3b/d94a75f4dbf1ef5d321523ecac21ef23a3cd2ac8b78ae2aac40873590229/numpy-2.2.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0b605b275d7bd0c640cad4e5d30fa701a8d59302e127e5f79138ad62762c3e3d", size = 21040391, upload-time = "2025-05-17T21:44:35.948Z" }, { url = "https://files.pythonhosted.org/packages/17/f4/09b2fa1b58f0fb4f7c7963a1649c64c4d315752240377ed74d9cd878f7b5/numpy-2.2.6-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:7befc596a7dc9da8a337f79802ee8adb30a552a94f792b9c9d18c840055907db", size = 6786754, upload-time = "2025-05-17T21:44:47.446Z" }, { url = "https://files.pythonhosted.org/packages/af/30/feba75f143bdc868a1cc3f44ccfa6c4b9ec522b36458e738cd00f67b573f/numpy-2.2.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce47521a4754c8f4593837384bd3424880629f718d87c5d44f8ed763edd63543", size = 16643476, upload-time = "2025-05-17T21:45:11.871Z" }, @@ -488,6 +586,28 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d4/75/5baed8cd867eabee8aad1e74d7197d73971d6a3d40c821f1848b8fab8b84/numpy-2.3.0-cp312-cp312-win32.whl", hash = "sha256:e6648078bdd974ef5d15cecc31b0c410e2e24178a6e10bf511e0557eed0f2570", size = 6318285, upload-time = "2025-06-07T14:43:02.052Z" }, { url = "https://files.pythonhosted.org/packages/bc/49/d5781eaa1a15acb3b3a3f49dc9e2ff18d92d0ce5c2976f4ab5c0a7360250/numpy-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:0898c67a58cdaaf29994bc0e2c65230fd4de0ac40afaf1584ed0b02cd74c6fdd", size = 12732594, upload-time = "2025-06-07T14:43:21.071Z" }, { url = "https://files.pythonhosted.org/packages/c2/1c/6d343e030815c7c97a1f9fbad00211b47717c7fe446834c224bd5311e6f1/numpy-2.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:bd8df082b6c4695753ad6193018c05aac465d634834dca47a3ae06d4bb22d9ea", size = 9891498, upload-time = "2025-06-07T14:43:36.332Z" }, + { url = "https://files.pythonhosted.org/packages/73/fc/1d67f751fd4dbafc5780244fe699bc4084268bad44b7c5deb0492473127b/numpy-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5754ab5595bfa2c2387d241296e0381c21f44a4b90a776c3c1d39eede13a746a", size = 20889633, upload-time = "2025-06-07T14:44:06.839Z" }, + { url = "https://files.pythonhosted.org/packages/e8/95/73ffdb69e5c3f19ec4530f8924c4386e7ba097efc94b9c0aff607178ad94/numpy-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d11fa02f77752d8099573d64e5fe33de3229b6632036ec08f7080f46b6649959", size = 14151683, upload-time = "2025-06-07T14:44:28.847Z" }, + { url = "https://files.pythonhosted.org/packages/64/d5/06d4bb31bb65a1d9c419eb5676173a2f90fd8da3c59f816cc54c640ce265/numpy-2.3.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:aba48d17e87688a765ab1cd557882052f238e2f36545dfa8e29e6a91aef77afe", size = 5102683, upload-time = "2025-06-07T14:44:38.417Z" }, + { url = "https://files.pythonhosted.org/packages/12/8b/6c2cef44f8ccdc231f6b56013dff1d71138c48124334aded36b1a1b30c5a/numpy-2.3.0-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:4dc58865623023b63b10d52f18abaac3729346a7a46a778381e0e3af4b7f3beb", size = 6640253, upload-time = "2025-06-07T14:44:49.359Z" }, + { url = "https://files.pythonhosted.org/packages/62/aa/fca4bf8de3396ddb59544df9b75ffe5b73096174de97a9492d426f5cd4aa/numpy-2.3.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:df470d376f54e052c76517393fa443758fefcdd634645bc9c1f84eafc67087f0", size = 14258658, upload-time = "2025-06-07T14:45:10.156Z" }, + { url = "https://files.pythonhosted.org/packages/1c/12/734dce1087eed1875f2297f687e671cfe53a091b6f2f55f0c7241aad041b/numpy-2.3.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:87717eb24d4a8a64683b7a4e91ace04e2f5c7c77872f823f02a94feee186168f", size = 16628765, upload-time = "2025-06-07T14:45:35.076Z" }, + { url = "https://files.pythonhosted.org/packages/48/03/ffa41ade0e825cbcd5606a5669962419528212a16082763fc051a7247d76/numpy-2.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d8fa264d56882b59dcb5ea4d6ab6f31d0c58a57b41aec605848b6eb2ef4a43e8", size = 15564335, upload-time = "2025-06-07T14:45:58.797Z" }, + { url = "https://files.pythonhosted.org/packages/07/58/869398a11863310aee0ff85a3e13b4c12f20d032b90c4b3ee93c3b728393/numpy-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e651756066a0eaf900916497e20e02fe1ae544187cb0fe88de981671ee7f6270", size = 18360608, upload-time = "2025-06-07T14:46:25.687Z" }, + { url = "https://files.pythonhosted.org/packages/2f/8a/5756935752ad278c17e8a061eb2127c9a3edf4ba2c31779548b336f23c8d/numpy-2.3.0-cp313-cp313-win32.whl", hash = "sha256:e43c3cce3b6ae5f94696669ff2a6eafd9a6b9332008bafa4117af70f4b88be6f", size = 6310005, upload-time = "2025-06-07T14:50:13.138Z" }, + { url = "https://files.pythonhosted.org/packages/08/60/61d60cf0dfc0bf15381eaef46366ebc0c1a787856d1db0c80b006092af84/numpy-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:81ae0bf2564cf475f94be4a27ef7bcf8af0c3e28da46770fc904da9abd5279b5", size = 12729093, upload-time = "2025-06-07T14:50:31.82Z" }, + { url = "https://files.pythonhosted.org/packages/66/31/2f2f2d2b3e3c32d5753d01437240feaa32220b73258c9eef2e42a0832866/numpy-2.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:c8738baa52505fa6e82778580b23f945e3578412554d937093eac9205e845e6e", size = 9885689, upload-time = "2025-06-07T14:50:47.888Z" }, + { url = "https://files.pythonhosted.org/packages/f1/89/c7828f23cc50f607ceb912774bb4cff225ccae7131c431398ad8400e2c98/numpy-2.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:39b27d8b38942a647f048b675f134dd5a567f95bfff481f9109ec308515c51d8", size = 20986612, upload-time = "2025-06-07T14:46:56.077Z" }, + { url = "https://files.pythonhosted.org/packages/dd/46/79ecf47da34c4c50eedec7511e53d57ffdfd31c742c00be7dc1d5ffdb917/numpy-2.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0eba4a1ea88f9a6f30f56fdafdeb8da3774349eacddab9581a21234b8535d3d3", size = 14298953, upload-time = "2025-06-07T14:47:18.053Z" }, + { url = "https://files.pythonhosted.org/packages/59/44/f6caf50713d6ff4480640bccb2a534ce1d8e6e0960c8f864947439f0ee95/numpy-2.3.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:b0f1f11d0a1da54927436505a5a7670b154eac27f5672afc389661013dfe3d4f", size = 5225806, upload-time = "2025-06-07T14:47:27.524Z" }, + { url = "https://files.pythonhosted.org/packages/a6/43/e1fd1aca7c97e234dd05e66de4ab7a5be54548257efcdd1bc33637e72102/numpy-2.3.0-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:690d0a5b60a47e1f9dcec7b77750a4854c0d690e9058b7bef3106e3ae9117808", size = 6735169, upload-time = "2025-06-07T14:47:38.057Z" }, + { url = "https://files.pythonhosted.org/packages/84/89/f76f93b06a03177c0faa7ca94d0856c4e5c4bcaf3c5f77640c9ed0303e1c/numpy-2.3.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:8b51ead2b258284458e570942137155978583e407babc22e3d0ed7af33ce06f8", size = 14330701, upload-time = "2025-06-07T14:47:59.113Z" }, + { url = "https://files.pythonhosted.org/packages/aa/f5/4858c3e9ff7a7d64561b20580cf7cc5d085794bd465a19604945d6501f6c/numpy-2.3.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:aaf81c7b82c73bd9b45e79cfb9476cb9c29e937494bfe9092c26aece812818ad", size = 16692983, upload-time = "2025-06-07T14:48:24.196Z" }, + { url = "https://files.pythonhosted.org/packages/08/17/0e3b4182e691a10e9483bcc62b4bb8693dbf9ea5dc9ba0b77a60435074bb/numpy-2.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:f420033a20b4f6a2a11f585f93c843ac40686a7c3fa514060a97d9de93e5e72b", size = 15641435, upload-time = "2025-06-07T14:48:47.712Z" }, + { url = "https://files.pythonhosted.org/packages/4e/d5/463279fda028d3c1efa74e7e8d507605ae87f33dbd0543cf4c4527c8b882/numpy-2.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d344ca32ab482bcf8735d8f95091ad081f97120546f3d250240868430ce52555", size = 18433798, upload-time = "2025-06-07T14:49:14.866Z" }, + { url = "https://files.pythonhosted.org/packages/0e/1e/7a9d98c886d4c39a2b4d3a7c026bffcf8fbcaf518782132d12a301cfc47a/numpy-2.3.0-cp313-cp313t-win32.whl", hash = "sha256:48a2e8eaf76364c32a1feaa60d6925eaf32ed7a040183b807e02674305beef61", size = 6438632, upload-time = "2025-06-07T14:49:25.67Z" }, + { url = "https://files.pythonhosted.org/packages/fe/ab/66fc909931d5eb230107d016861824f335ae2c0533f422e654e5ff556784/numpy-2.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ba17f93a94e503551f154de210e4d50c5e3ee20f7e7a1b5f6ce3f22d419b93bb", size = 12868491, upload-time = "2025-06-07T14:49:44.898Z" }, + { url = "https://files.pythonhosted.org/packages/ee/e8/2c8a1c9e34d6f6d600c83d5ce5b71646c32a13f34ca5c518cc060639841c/numpy-2.3.0-cp313-cp313t-win_arm64.whl", hash = "sha256:f14e016d9409680959691c109be98c436c6249eaf7f118b424679793607b5944", size = 9935345, upload-time = "2025-06-07T14:50:02.311Z" }, { url = "https://files.pythonhosted.org/packages/6a/a2/f8c1133f90eaa1c11bbbec1dc28a42054d0ce74bc2c9838c5437ba5d4980/numpy-2.3.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:80b46117c7359de8167cc00a2c7d823bdd505e8c7727ae0871025a86d668283b", size = 21070759, upload-time = "2025-06-07T14:51:18.241Z" }, { url = "https://files.pythonhosted.org/packages/6c/e0/4c05fc44ba28463096eee5ae2a12832c8d2759cc5bcec34ae33386d3ff83/numpy-2.3.0-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:5814a0f43e70c061f47abd5857d120179609ddc32a613138cbb6c4e9e2dbdda5", size = 5301054, upload-time = "2025-06-07T14:51:27.413Z" }, { url = "https://files.pythonhosted.org/packages/8a/3b/6c06cdebe922bbc2a466fe2105f50f661238ea223972a69c7deb823821e7/numpy-2.3.0-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:ef6c1e88fd6b81ac6d215ed71dc8cd027e54d4bf1d2682d362449097156267a2", size = 6817520, upload-time = "2025-06-07T14:51:38.015Z" }, @@ -544,6 +664,28 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/96/ae/ca0099a3995976a9fce2f423166f7bff9b12244afdc7520f6ed38911539a/pillow-11.2.1-cp312-cp312-win32.whl", hash = "sha256:1d535df14716e7f8776b9e7fee118576d65572b4aad3ed639be9e4fa88a1cad3", size = 2332309, upload-time = "2025-04-12T17:48:17.885Z" }, { url = "https://files.pythonhosted.org/packages/7c/18/24bff2ad716257fc03da964c5e8f05d9790a779a8895d6566e493ccf0189/pillow-11.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:14e33b28bf17c7a38eede290f77db7c664e4eb01f7869e37fa98a5aa95978941", size = 2676768, upload-time = "2025-04-12T17:48:19.655Z" }, { url = "https://files.pythonhosted.org/packages/da/bb/e8d656c9543276517ee40184aaa39dcb41e683bca121022f9323ae11b39d/pillow-11.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:21e1470ac9e5739ff880c211fc3af01e3ae505859392bf65458c224d0bf283eb", size = 2415087, upload-time = "2025-04-12T17:48:21.991Z" }, + { url = "https://files.pythonhosted.org/packages/36/9c/447528ee3776e7ab8897fe33697a7ff3f0475bb490c5ac1456a03dc57956/pillow-11.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:fdec757fea0b793056419bca3e9932eb2b0ceec90ef4813ea4c1e072c389eb28", size = 3190098, upload-time = "2025-04-12T17:48:23.915Z" }, + { url = "https://files.pythonhosted.org/packages/b5/09/29d5cd052f7566a63e5b506fac9c60526e9ecc553825551333e1e18a4858/pillow-11.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b0e130705d568e2f43a17bcbe74d90958e8a16263868a12c3e0d9c8162690830", size = 3030166, upload-time = "2025-04-12T17:48:25.738Z" }, + { url = "https://files.pythonhosted.org/packages/71/5d/446ee132ad35e7600652133f9c2840b4799bbd8e4adba881284860da0a36/pillow-11.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bdb5e09068332578214cadd9c05e3d64d99e0e87591be22a324bdbc18925be0", size = 4408674, upload-time = "2025-04-12T17:48:27.908Z" }, + { url = "https://files.pythonhosted.org/packages/69/5f/cbe509c0ddf91cc3a03bbacf40e5c2339c4912d16458fcb797bb47bcb269/pillow-11.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d189ba1bebfbc0c0e529159631ec72bb9e9bc041f01ec6d3233d6d82eb823bc1", size = 4496005, upload-time = "2025-04-12T17:48:29.888Z" }, + { url = "https://files.pythonhosted.org/packages/f9/b3/dd4338d8fb8a5f312021f2977fb8198a1184893f9b00b02b75d565c33b51/pillow-11.2.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:191955c55d8a712fab8934a42bfefbf99dd0b5875078240943f913bb66d46d9f", size = 4518707, upload-time = "2025-04-12T17:48:31.874Z" }, + { url = "https://files.pythonhosted.org/packages/13/eb/2552ecebc0b887f539111c2cd241f538b8ff5891b8903dfe672e997529be/pillow-11.2.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:ad275964d52e2243430472fc5d2c2334b4fc3ff9c16cb0a19254e25efa03a155", size = 4610008, upload-time = "2025-04-12T17:48:34.422Z" }, + { url = "https://files.pythonhosted.org/packages/72/d1/924ce51bea494cb6e7959522d69d7b1c7e74f6821d84c63c3dc430cbbf3b/pillow-11.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:750f96efe0597382660d8b53e90dd1dd44568a8edb51cb7f9d5d918b80d4de14", size = 4585420, upload-time = "2025-04-12T17:48:37.641Z" }, + { url = "https://files.pythonhosted.org/packages/43/ab/8f81312d255d713b99ca37479a4cb4b0f48195e530cdc1611990eb8fd04b/pillow-11.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fe15238d3798788d00716637b3d4e7bb6bde18b26e5d08335a96e88564a36b6b", size = 4667655, upload-time = "2025-04-12T17:48:39.652Z" }, + { url = "https://files.pythonhosted.org/packages/94/86/8f2e9d2dc3d308dfd137a07fe1cc478df0a23d42a6c4093b087e738e4827/pillow-11.2.1-cp313-cp313-win32.whl", hash = "sha256:3fe735ced9a607fee4f481423a9c36701a39719252a9bb251679635f99d0f7d2", size = 2332329, upload-time = "2025-04-12T17:48:41.765Z" }, + { url = "https://files.pythonhosted.org/packages/6d/ec/1179083b8d6067a613e4d595359b5fdea65d0a3b7ad623fee906e1b3c4d2/pillow-11.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:74ee3d7ecb3f3c05459ba95eed5efa28d6092d751ce9bf20e3e253a4e497e691", size = 2676388, upload-time = "2025-04-12T17:48:43.625Z" }, + { url = "https://files.pythonhosted.org/packages/23/f1/2fc1e1e294de897df39fa8622d829b8828ddad938b0eaea256d65b84dd72/pillow-11.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:5119225c622403afb4b44bad4c1ca6c1f98eed79db8d3bc6e4e160fc6339d66c", size = 2414950, upload-time = "2025-04-12T17:48:45.475Z" }, + { url = "https://files.pythonhosted.org/packages/c4/3e/c328c48b3f0ead7bab765a84b4977acb29f101d10e4ef57a5e3400447c03/pillow-11.2.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:8ce2e8411c7aaef53e6bb29fe98f28cd4fbd9a1d9be2eeea434331aac0536b22", size = 3192759, upload-time = "2025-04-12T17:48:47.866Z" }, + { url = "https://files.pythonhosted.org/packages/18/0e/1c68532d833fc8b9f404d3a642991441d9058eccd5606eab31617f29b6d4/pillow-11.2.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:9ee66787e095127116d91dea2143db65c7bb1e232f617aa5957c0d9d2a3f23a7", size = 3033284, upload-time = "2025-04-12T17:48:50.189Z" }, + { url = "https://files.pythonhosted.org/packages/b7/cb/6faf3fb1e7705fd2db74e070f3bf6f88693601b0ed8e81049a8266de4754/pillow-11.2.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9622e3b6c1d8b551b6e6f21873bdcc55762b4b2126633014cea1803368a9aa16", size = 4445826, upload-time = "2025-04-12T17:48:52.346Z" }, + { url = "https://files.pythonhosted.org/packages/07/94/8be03d50b70ca47fb434a358919d6a8d6580f282bbb7af7e4aa40103461d/pillow-11.2.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63b5dff3a68f371ea06025a1a6966c9a1e1ee452fc8020c2cd0ea41b83e9037b", size = 4527329, upload-time = "2025-04-12T17:48:54.403Z" }, + { url = "https://files.pythonhosted.org/packages/fd/a4/bfe78777076dc405e3bd2080bc32da5ab3945b5a25dc5d8acaa9de64a162/pillow-11.2.1-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:31df6e2d3d8fc99f993fd253e97fae451a8db2e7207acf97859732273e108406", size = 4549049, upload-time = "2025-04-12T17:48:56.383Z" }, + { url = "https://files.pythonhosted.org/packages/65/4d/eaf9068dc687c24979e977ce5677e253624bd8b616b286f543f0c1b91662/pillow-11.2.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:062b7a42d672c45a70fa1f8b43d1d38ff76b63421cbbe7f88146b39e8a558d91", size = 4635408, upload-time = "2025-04-12T17:48:58.782Z" }, + { url = "https://files.pythonhosted.org/packages/1d/26/0fd443365d9c63bc79feb219f97d935cd4b93af28353cba78d8e77b61719/pillow-11.2.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4eb92eca2711ef8be42fd3f67533765d9fd043b8c80db204f16c8ea62ee1a751", size = 4614863, upload-time = "2025-04-12T17:49:00.709Z" }, + { url = "https://files.pythonhosted.org/packages/49/65/dca4d2506be482c2c6641cacdba5c602bc76d8ceb618fd37de855653a419/pillow-11.2.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f91ebf30830a48c825590aede79376cb40f110b387c17ee9bd59932c961044f9", size = 4692938, upload-time = "2025-04-12T17:49:02.946Z" }, + { url = "https://files.pythonhosted.org/packages/b3/92/1ca0c3f09233bd7decf8f7105a1c4e3162fb9142128c74adad0fb361b7eb/pillow-11.2.1-cp313-cp313t-win32.whl", hash = "sha256:e0b55f27f584ed623221cfe995c912c61606be8513bfa0e07d2c674b4516d9dd", size = 2335774, upload-time = "2025-04-12T17:49:04.889Z" }, + { url = "https://files.pythonhosted.org/packages/a5/ac/77525347cb43b83ae905ffe257bbe2cc6fd23acb9796639a1f56aa59d191/pillow-11.2.1-cp313-cp313t-win_amd64.whl", hash = "sha256:36d6b82164c39ce5482f649b437382c0fb2395eabc1e2b1702a6deb8ad647d6e", size = 2681895, upload-time = "2025-04-12T17:49:06.635Z" }, + { url = "https://files.pythonhosted.org/packages/67/32/32dc030cfa91ca0fc52baebbba2e009bb001122a1daa8b6a79ad830b38d3/pillow-11.2.1-cp313-cp313t-win_arm64.whl", hash = "sha256:225c832a13326e34f212d2072982bb1adb210e0cc0b153e688743018c94a2681", size = 2417234, upload-time = "2025-04-12T17:49:08.399Z" }, { url = "https://files.pythonhosted.org/packages/33/49/c8c21e4255b4f4a2c0c68ac18125d7f5460b109acc6dfdef1a24f9b960ef/pillow-11.2.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:9b7b0d4fd2635f54ad82785d56bc0d94f147096493a79985d0ab57aedd563156", size = 3181727, upload-time = "2025-04-12T17:49:31.898Z" }, { url = "https://files.pythonhosted.org/packages/6d/f1/f7255c0838f8c1ef6d55b625cfb286835c17e8136ce4351c5577d02c443b/pillow-11.2.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:aa442755e31c64037aa7c1cb186e0b369f8416c567381852c63444dd666fb772", size = 2999833, upload-time = "2025-04-12T17:49:34.2Z" }, { url = "https://files.pythonhosted.org/packages/e2/57/9968114457bd131063da98d87790d080366218f64fa2943b65ac6739abb3/pillow-11.2.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0d3348c95b766f54b76116d53d4cb171b52992a1027e7ca50c81b43b9d9e363", size = 3437472, upload-time = "2025-04-12T17:49:36.294Z" }, @@ -573,19 +715,20 @@ wheels = [ name = "plutho" source = { editable = "." } dependencies = [ - { name = "flake8" }, { name = "gmsh" }, { name = "matplotlib" }, { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "numpy", version = "2.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "python-dotenv" }, { name = "pyyaml" }, - { name = "ruff" }, { name = "scipy" }, ] -[package.optional-dependencies] -doc = [ +[package.dev-dependencies] +lint = [ + { name = "ruff" }, +] +ruff = [ { name = "setuptools-scm" }, { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, @@ -595,52 +738,24 @@ test = [ { name = "pytest" }, ] -[package.dev-dependencies] -dev = [ - { name = "plutho" }, - { name = "pytest" }, -] - [package.metadata] requires-dist = [ - { name = "flake8", specifier = ">=7.2.0" }, - { name = "gmsh" }, + { name = "gmsh", specifier = "==4.13.1" }, { name = "matplotlib" }, { name = "numpy" }, - { name = "pytest", marker = "extra == 'test'" }, { name = "python-dotenv" }, { name = "pyyaml" }, - { name = "ruff", specifier = ">=0.12.0" }, { name = "scipy", specifier = ">=1.12.0" }, - { name = "setuptools-scm", marker = "extra == 'doc'" }, - { name = "sphinx", marker = "extra == 'doc'" }, - { name = "sphinx-rtd-theme", marker = "extra == 'doc'" }, ] -provides-extras = ["doc", "test"] [package.metadata.requires-dev] -dev = [ - { name = "plutho", editable = "." }, - { name = "pytest", specifier = ">=8.4.1" }, -] - -[[package]] -name = "pycodestyle" -version = "2.13.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/04/6e/1f4a62078e4d95d82367f24e685aef3a672abfd27d1a868068fed4ed2254/pycodestyle-2.13.0.tar.gz", hash = "sha256:c8415bf09abe81d9c7f872502a6eee881fbe85d8763dd5b9924bb0a01d67efae", size = 39312, upload-time = "2025-03-29T17:33:30.669Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/07/be/b00116df1bfb3e0bb5b45e29d604799f7b91dd861637e4d448b4e09e6a3e/pycodestyle-2.13.0-py2.py3-none-any.whl", hash = "sha256:35863c5974a271c7a726ed228a14a4f6daf49df369d8c50cd9a6f58a5e143ba9", size = 31424, upload-time = "2025-03-29T17:33:29.405Z" }, -] - -[[package]] -name = "pyflakes" -version = "3.3.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/af/cc/1df338bd7ed1fa7c317081dcf29bf2f01266603b301e6858856d346a12b3/pyflakes-3.3.2.tar.gz", hash = "sha256:6dfd61d87b97fba5dcfaaf781171ac16be16453be6d816147989e7f6e6a9576b", size = 64175, upload-time = "2025-03-31T13:21:20.34Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/15/40/b293a4fa769f3b02ab9e387c707c4cbdc34f073f945de0386107d4e669e6/pyflakes-3.3.2-py2.py3-none-any.whl", hash = "sha256:5039c8339cbb1944045f4ee5466908906180f13cc99cc9949348d10f82a5c32a", size = 63164, upload-time = "2025-03-31T13:21:18.503Z" }, +lint = [{ name = "ruff" }] +ruff = [ + { name = "setuptools-scm" }, + { name = "sphinx" }, + { name = "sphinx-rtd-theme" }, ] +test = [{ name = "pytest" }] [[package]] name = "pygments" @@ -732,6 +847,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611, upload-time = "2024-08-06T20:32:38.898Z" }, { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591, upload-time = "2024-08-06T20:32:40.241Z" }, { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338, upload-time = "2024-08-06T20:32:41.93Z" }, + { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309, upload-time = "2024-08-06T20:32:43.4Z" }, + { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679, upload-time = "2024-08-06T20:32:44.801Z" }, + { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428, upload-time = "2024-08-06T20:32:46.432Z" }, + { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361, upload-time = "2024-08-06T20:32:51.188Z" }, + { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523, upload-time = "2024-08-06T20:32:53.019Z" }, + { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660, upload-time = "2024-08-06T20:32:54.708Z" }, + { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597, upload-time = "2024-08-06T20:32:56.985Z" }, + { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527, upload-time = "2024-08-06T20:33:03.001Z" }, + { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446, upload-time = "2024-08-06T20:33:04.33Z" }, ] [[package]] @@ -820,6 +944,24 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/89/b1/fbb53137f42c4bf630b1ffdfc2151a62d1d1b903b249f030d2b1c0280af8/scipy-1.15.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6cfd56fc1a8e53f6e89ba3a7a7251f7396412d655bca2aa5611c8ec9a6784a1e", size = 36885140, upload-time = "2025-05-08T16:06:39.249Z" }, { url = "https://files.pythonhosted.org/packages/2e/2e/025e39e339f5090df1ff266d021892694dbb7e63568edcfe43f892fa381d/scipy-1.15.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0ff17c0bb1cb32952c09217d8d1eed9b53d1463e5f1dd6052c7857f83127d539", size = 39710549, upload-time = "2025-05-08T16:06:45.729Z" }, { url = "https://files.pythonhosted.org/packages/e6/eb/3bf6ea8ab7f1503dca3a10df2e4b9c3f6b3316df07f6c0ded94b281c7101/scipy-1.15.3-cp312-cp312-win_amd64.whl", hash = "sha256:52092bc0472cfd17df49ff17e70624345efece4e1a12b23783a1ac59a1b728ed", size = 40966184, upload-time = "2025-05-08T16:06:52.623Z" }, + { url = "https://files.pythonhosted.org/packages/73/18/ec27848c9baae6e0d6573eda6e01a602e5649ee72c27c3a8aad673ebecfd/scipy-1.15.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2c620736bcc334782e24d173c0fdbb7590a0a436d2fdf39310a8902505008759", size = 38728256, upload-time = "2025-05-08T16:06:58.696Z" }, + { url = "https://files.pythonhosted.org/packages/74/cd/1aef2184948728b4b6e21267d53b3339762c285a46a274ebb7863c9e4742/scipy-1.15.3-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:7e11270a000969409d37ed399585ee530b9ef6aa99d50c019de4cb01e8e54e62", size = 30109540, upload-time = "2025-05-08T16:07:04.209Z" }, + { url = "https://files.pythonhosted.org/packages/5b/d8/59e452c0a255ec352bd0a833537a3bc1bfb679944c4938ab375b0a6b3a3e/scipy-1.15.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:8c9ed3ba2c8a2ce098163a9bdb26f891746d02136995df25227a20e71c396ebb", size = 22383115, upload-time = "2025-05-08T16:07:08.998Z" }, + { url = "https://files.pythonhosted.org/packages/08/f5/456f56bbbfccf696263b47095291040655e3cbaf05d063bdc7c7517f32ac/scipy-1.15.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:0bdd905264c0c9cfa74a4772cdb2070171790381a5c4d312c973382fc6eaf730", size = 25163884, upload-time = "2025-05-08T16:07:14.091Z" }, + { url = "https://files.pythonhosted.org/packages/a2/66/a9618b6a435a0f0c0b8a6d0a2efb32d4ec5a85f023c2b79d39512040355b/scipy-1.15.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79167bba085c31f38603e11a267d862957cbb3ce018d8b38f79ac043bc92d825", size = 35174018, upload-time = "2025-05-08T16:07:19.427Z" }, + { url = "https://files.pythonhosted.org/packages/b5/09/c5b6734a50ad4882432b6bb7c02baf757f5b2f256041da5df242e2d7e6b6/scipy-1.15.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9deabd6d547aee2c9a81dee6cc96c6d7e9a9b1953f74850c179f91fdc729cb7", size = 37269716, upload-time = "2025-05-08T16:07:25.712Z" }, + { url = "https://files.pythonhosted.org/packages/77/0a/eac00ff741f23bcabd352731ed9b8995a0a60ef57f5fd788d611d43d69a1/scipy-1.15.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:dde4fc32993071ac0c7dd2d82569e544f0bdaff66269cb475e0f369adad13f11", size = 36872342, upload-time = "2025-05-08T16:07:31.468Z" }, + { url = "https://files.pythonhosted.org/packages/fe/54/4379be86dd74b6ad81551689107360d9a3e18f24d20767a2d5b9253a3f0a/scipy-1.15.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f77f853d584e72e874d87357ad70f44b437331507d1c311457bed8ed2b956126", size = 39670869, upload-time = "2025-05-08T16:07:38.002Z" }, + { url = "https://files.pythonhosted.org/packages/87/2e/892ad2862ba54f084ffe8cc4a22667eaf9c2bcec6d2bff1d15713c6c0703/scipy-1.15.3-cp313-cp313-win_amd64.whl", hash = "sha256:b90ab29d0c37ec9bf55424c064312930ca5f4bde15ee8619ee44e69319aab163", size = 40988851, upload-time = "2025-05-08T16:08:33.671Z" }, + { url = "https://files.pythonhosted.org/packages/1b/e9/7a879c137f7e55b30d75d90ce3eb468197646bc7b443ac036ae3fe109055/scipy-1.15.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3ac07623267feb3ae308487c260ac684b32ea35fd81e12845039952f558047b8", size = 38863011, upload-time = "2025-05-08T16:07:44.039Z" }, + { url = "https://files.pythonhosted.org/packages/51/d1/226a806bbd69f62ce5ef5f3ffadc35286e9fbc802f606a07eb83bf2359de/scipy-1.15.3-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:6487aa99c2a3d509a5227d9a5e889ff05830a06b2ce08ec30df6d79db5fcd5c5", size = 30266407, upload-time = "2025-05-08T16:07:49.891Z" }, + { url = "https://files.pythonhosted.org/packages/e5/9b/f32d1d6093ab9eeabbd839b0f7619c62e46cc4b7b6dbf05b6e615bbd4400/scipy-1.15.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:50f9e62461c95d933d5c5ef4a1f2ebf9a2b4e83b0db374cb3f1de104d935922e", size = 22540030, upload-time = "2025-05-08T16:07:54.121Z" }, + { url = "https://files.pythonhosted.org/packages/e7/29/c278f699b095c1a884f29fda126340fcc201461ee8bfea5c8bdb1c7c958b/scipy-1.15.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:14ed70039d182f411ffc74789a16df3835e05dc469b898233a245cdfd7f162cb", size = 25218709, upload-time = "2025-05-08T16:07:58.506Z" }, + { url = "https://files.pythonhosted.org/packages/24/18/9e5374b617aba742a990581373cd6b68a2945d65cc588482749ef2e64467/scipy-1.15.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a769105537aa07a69468a0eefcd121be52006db61cdd8cac8a0e68980bbb723", size = 34809045, upload-time = "2025-05-08T16:08:03.929Z" }, + { url = "https://files.pythonhosted.org/packages/e1/fe/9c4361e7ba2927074360856db6135ef4904d505e9b3afbbcb073c4008328/scipy-1.15.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9db984639887e3dffb3928d118145ffe40eff2fa40cb241a306ec57c219ebbbb", size = 36703062, upload-time = "2025-05-08T16:08:09.558Z" }, + { url = "https://files.pythonhosted.org/packages/b7/8e/038ccfe29d272b30086b25a4960f757f97122cb2ec42e62b460d02fe98e9/scipy-1.15.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:40e54d5c7e7ebf1aa596c374c49fa3135f04648a0caabcb66c52884b943f02b4", size = 36393132, upload-time = "2025-05-08T16:08:15.34Z" }, + { url = "https://files.pythonhosted.org/packages/10/7e/5c12285452970be5bdbe8352c619250b97ebf7917d7a9a9e96b8a8140f17/scipy-1.15.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5e721fed53187e71d0ccf382b6bf977644c533e506c4d33c3fb24de89f5c3ed5", size = 38979503, upload-time = "2025-05-08T16:08:21.513Z" }, + { url = "https://files.pythonhosted.org/packages/81/06/0a5e5349474e1cbc5757975b21bd4fad0e72ebf138c5592f191646154e06/scipy-1.15.3-cp313-cp313t-win_amd64.whl", hash = "sha256:76ad1fb5f8752eabf0fa02e4cc0336b4e8f021e2d5f061ed37d6d264db35e3ca", size = 40308097, upload-time = "2025-05-08T16:08:27.627Z" }, ] [[package]] @@ -1033,6 +1175,16 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", size = 244584, upload-time = "2024-11-27T22:38:17.645Z" }, { url = "https://files.pythonhosted.org/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875, upload-time = "2024-11-27T22:38:19.159Z" }, { url = "https://files.pythonhosted.org/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418, upload-time = "2024-11-27T22:38:20.064Z" }, + { url = "https://files.pythonhosted.org/packages/04/90/2ee5f2e0362cb8a0b6499dc44f4d7d48f8fff06d28ba46e6f1eaa61a1388/tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7", size = 132708, upload-time = "2024-11-27T22:38:21.659Z" }, + { url = "https://files.pythonhosted.org/packages/c0/ec/46b4108816de6b385141f082ba99e315501ccd0a2ea23db4a100dd3990ea/tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", size = 123582, upload-time = "2024-11-27T22:38:22.693Z" }, + { url = "https://files.pythonhosted.org/packages/a0/bd/b470466d0137b37b68d24556c38a0cc819e8febe392d5b199dcd7f578365/tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", size = 232543, upload-time = "2024-11-27T22:38:24.367Z" }, + { url = "https://files.pythonhosted.org/packages/d9/e5/82e80ff3b751373f7cead2815bcbe2d51c895b3c990686741a8e56ec42ab/tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", size = 241691, upload-time = "2024-11-27T22:38:26.081Z" }, + { url = "https://files.pythonhosted.org/packages/05/7e/2a110bc2713557d6a1bfb06af23dd01e7dde52b6ee7dadc589868f9abfac/tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", size = 251170, upload-time = "2024-11-27T22:38:27.921Z" }, + { url = "https://files.pythonhosted.org/packages/64/7b/22d713946efe00e0adbcdfd6d1aa119ae03fd0b60ebed51ebb3fa9f5a2e5/tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", size = 236530, upload-time = "2024-11-27T22:38:29.591Z" }, + { url = "https://files.pythonhosted.org/packages/38/31/3a76f67da4b0cf37b742ca76beaf819dca0ebef26d78fc794a576e08accf/tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", size = 258666, upload-time = "2024-11-27T22:38:30.639Z" }, + { url = "https://files.pythonhosted.org/packages/07/10/5af1293da642aded87e8a988753945d0cf7e00a9452d3911dd3bb354c9e2/tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", size = 243954, upload-time = "2024-11-27T22:38:31.702Z" }, + { url = "https://files.pythonhosted.org/packages/5b/b9/1ed31d167be802da0fc95020d04cd27b7d7065cc6fbefdd2f9186f60d7bd/tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", size = 98724, upload-time = "2024-11-27T22:38:32.837Z" }, + { url = "https://files.pythonhosted.org/packages/c7/32/b0963458706accd9afcfeb867c0f9175a741bf7b19cd424230714d722198/tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", size = 109383, upload-time = "2024-11-27T22:38:34.455Z" }, { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257, upload-time = "2024-11-27T22:38:35.385Z" }, ] From f42a1cca979dc02375306f9576a7b7bde1a1b5d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20H=C3=B6lscher?= Date: Thu, 15 Jan 2026 13:39:14 +0100 Subject: [PATCH 16/22] Testing jacobian for Piezo_HB --- plutho/simulations/nonlinear/piezo_hb.py | 124 ++++++++++++++++++----- 1 file changed, 100 insertions(+), 24 deletions(-) diff --git a/plutho/simulations/nonlinear/piezo_hb.py b/plutho/simulations/nonlinear/piezo_hb.py index 29513a6..813dd52 100644 --- a/plutho/simulations/nonlinear/piezo_hb.py +++ b/plutho/simulations/nonlinear/piezo_hb.py @@ -156,9 +156,9 @@ def newton_arclength( u_last: npt.NDArray, freq_last: float, freq_load_last: float, - load_vector: npt.NDArray, - tangent_fun: Callable, - rhs_fun: Callable, + tangent_u_fun: Callable, + tangent_vec_freq_load_fun: Callable, + residual_fun: Callable, tolerance: float, max_iter: int ): @@ -166,34 +166,32 @@ def newton_arclength( u_0 = u_last # Predictor step - lu = slin.splu(tangent_fun(u_0, freq_last, freq_load_last)) - delta_u_p_0 = lu.solve(load_vector) + lu = slin.splu(tangent_u_fun(u_0, freq_last, freq_load_last)) + P = -tangent_vec_freq_load_fun(u_last, freq_last, freq_load_last) + delta_u_p_0 = lu.solve(P) u_p = u_last + delta_u_p_0 # Compute frequency load increment - arc_length = 1000 * np.linalg.norm(u_p) + arc_length = 1.1 delta_freq_load_0 = arc_length/(np.linalg.norm(delta_u_p_0)) ## Calculate direction - current_stiffness_parameter = load_vector@u_p/np.linalg.norm(u_p) + current_stiffness_parameter = P@u_p/np.linalg.norm(u_p) if current_stiffness_parameter < 0: delta_freq_load_0 *= -1 freq_load_0 = freq_load_last + delta_freq_load_0 # Check if initial value is already sufficient - residual = rhs_fun(u_0, freq_last, freq_load_0) + residual = residual_fun(u_0, freq_last, freq_load_0) norm = np.linalg.norm(residual) if norm < tolerance: print("Initial value already sufficient") - return u_0 + return u_0, freq_load_0 # Set initial values for iteraion u_i = u_0 freq_load_i = freq_load_0 - for i in range(max_iter): - # Residual function - def G(u, freq_load): - return rhs_fun(u, freq_last, freq_load) - load_vector + for i in range(max_iter): # RIKS arc-length constraint def riks(u, freq_load): return ( @@ -209,9 +207,39 @@ def riks_dload(): def riks_du(): return u_0-u_last + # tmp: Test Tangent matrix + k_tan = tangent_u_fun(u_i, freq_last, freq_load_i) + P = tangent_vec_freq_load_fun(u_i, freq_last, freq_load_i) + P_mat = P.reshape(-1, 1) + du = riks_du() + du_mat = sparse.csc_matrix(du.reshape(1, -1)) + dl_mat = sparse.csc_matrix([[riks_dload()]]) + blocks = [ + [k_tan, -P_mat], + [du_mat, dl_mat] + ] + t = sparse.block_array(blocks, format="csc") + x = np.zeros(u_0.shape[0]+1) + x[:-1] = u_i + x[-1] = freq_load_i + + def res_fun(u): + x = np.zeros(u.shape[0]) + x[:-1] = residual_fun(u[:-1], freq_last, freq_load_i) + x[-1] = riks(u[:-1], freq_load_i) + return x + + print("Testing jacobian") + NLPiezoHB.test_jacobian_static( + t, + x, + res_fun + ) + # Solve for increments - u_p_next = lu.solve(load_vector) - u_g_next = lu.solve(-G(u_i, freq_load_i)) + P = -tangent_vec_freq_load_fun(u_i, freq_last, freq_load_i) + u_p_next = lu.solve(P) + u_g_next = lu.solve(-residual_fun(u_i, freq_last, freq_load_i)) # Calculate increments denominator = riks_dload()+riks_du()@u_p_next @@ -230,7 +258,7 @@ def riks_du(): u_i_next = u_i + delta_u_i # Update residual - residual = G(u_i_next, freq_load_i_next) + residual = residual_fun(u_i_next, freq_last, freq_load_i_next) # Check for convergence norm = np.linalg.norm(residual) @@ -245,7 +273,7 @@ def riks_du(): # Update for next iteration u_i = u_i_next freq_load_i = freq_load_i_next - lu = slin.splu(tangent_fun(u_i, freq_last, freq_load_i)) + lu = slin.splu(tangent_u_fun(u_i, freq_last, freq_load_i)) print(f"Newton did not converged: Maximum iteration (norm: {norm})") return u_i, freq_load_i @@ -322,18 +350,23 @@ def residual_fun(u, frequency, frequency_load): frequency ) - def rhs_arc_len_fun(u, frequency, frequency_load): - return residual_fun(u, frequency, frequency_load) + load_vector - def tangent_fun(u, frequency, frequency_load): angular_frequency = 2*np.pi*frequency*frequency_load return ( k_tan - + angular_frequency * c_tan - + angular_frequency**2 * m_tan + + angular_frequency*c_tan + + angular_frequency**2*m_tan + self.tangent_nonlinear(u, frequency) ).tocsc() + def dresidual_df(u, frequency, frequency_load): + # Residual derived after frequency load factor + angular_frequency = 2*np.pi*frequency + return ( + 2*frequency_load*angular_frequency**2*m_tan + + angular_frequency*c_tan + )@u + if index > 0: u_last = u[index-1, :] else: @@ -348,9 +381,9 @@ def tangent_fun(u, frequency, frequency_load): u_last, frequencies[index], frequency_loads[index], - load_vector, tangent_fun, - rhs_arc_len_fun, + dresidual_df, + residual_fun, tolerance, max_iter ) @@ -775,6 +808,49 @@ def _apply_dirichlet_bc( return m.tocsc(), c.tocsc(), k.tocsc() + @staticmethod + def test_jacobian_static( + k_tan_analytic, + u, + residual_fun, + epsilon: float = 1e-6 + ) -> Tuple[float, float]: + """Tests the analytical jacobian matrix with a jacobian calculated from + a finite difference approximation using the given u and epsilon. + + Parameters: + u: Example u vector. + + Returns: + Tuple of absolute and relative errors. + """ + # TODO: Rework and make class independent + # Create test data + # Finite difference jacobian + n = len(u) + J_fd = np.zeros((n, n)) + + for i in range(n): + u_plus = u.copy() + u_plus[i] += epsilon + Fp = residual_fun(u_plus) + + u_minus = u.copy() + u_minus[i] -= epsilon + Fm = residual_fun(u_minus) + + J_fd[:, i] = (Fp - Fm) / (2*epsilon) + + # Vergleiche + diff = np.abs(k_tan_analytic.toarray() - J_fd) + max_diff = np.max(diff) + rel_diff = max_diff / (np.max(np.abs(J_fd)) + 1e-10) + + print(f"Max absolute difference: {max_diff}") + print(f"Max relative difference: {rel_diff}") + + return max_diff, rel_diff + def test_jacobian(self, epsilon: float = 1e-6) -> Tuple[float, float]: """Tests the analytical jacobian matrix with a jacobian calculated from a finite difference approximation using the given u and epsilon. From efd9085bb3b0623e41879ed028e086c59d74c5ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20H=C3=B6lscher?= Date: Thu, 15 Jan 2026 13:40:57 +0100 Subject: [PATCH 17/22] Try LU for linear simulations --- plutho/simulations/piezo_time.py | 15 ++++++++------- plutho/simulations/thermo_piezo_time.py | 10 ++++++---- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/plutho/simulations/piezo_time.py b/plutho/simulations/piezo_time.py index 19e6e4c..518af5e 100644 --- a/plutho/simulations/piezo_time.py +++ b/plutho/simulations/piezo_time.py @@ -203,7 +203,8 @@ def simulate( dirichlet_nodes ) - k_star = (k+gamma/(beta*delta_t)*c+1/(beta*delta_t**2)*m).tocsr() + k_star = (k+gamma/(beta*delta_t)*c+1/(beta*delta_t**2)*m).tocsc() + lu = slin.splu(k_star) print("Starting simulation") for time_index in range(number_of_time_steps-1): @@ -224,12 +225,12 @@ def simulate( ) # Solve for next time step - u[time_index+1, :] = slin.spsolve( - k_star, ( - f - - c*v_tilde - + (1/(beta*delta_t**2)*m - + gamma/(beta*delta_t)*c)*u_tilde)) + u[time_index+1, :] = lu.solve( + f + - c*v_tilde + + (1/(beta*delta_t**2)*m + + gamma/(beta*delta_t)*c)*u_tilde + ) # Perform corrector step a[time_index+1, :] = (u[time_index+1, :]-u_tilde)/(beta*delta_t**2) v[time_index+1, :] = v_tilde + gamma*delta_t*a[time_index+1, :] diff --git a/plutho/simulations/thermo_piezo_time.py b/plutho/simulations/thermo_piezo_time.py index cbe95d3..2400b6a 100644 --- a/plutho/simulations/thermo_piezo_time.py +++ b/plutho/simulations/thermo_piezo_time.py @@ -238,7 +238,8 @@ def simulate( self.dirichlet_nodes ) - k_star = (k+gamma/(beta*delta_t)*c+1/(beta*delta_t**2)*m).tocsr() + k_star = (k+gamma/(beta*delta_t)*c+1/(beta*delta_t**2)*m).tocsc() + lu = slin.splu(k_star) # Mechanical loss calculated during simulation (for thermal field) mech_loss = np.zeros( @@ -303,12 +304,13 @@ def simulate( ) # Solve for next time step - u[time_index+1, :] = slin.spsolve( - k_star, ( + u[time_index+1, :] = lu.solve( + ( f - c*v_tilde + (1/(beta*delta_t**2)*m - + gamma/(beta*delta_t)*c)*u_tilde) + + gamma/(beta*delta_t)*c)*u_tilde + ) ) # Perform corrector step a[time_index+1, :] = (u[time_index+1, :]-u_tilde)/(beta*delta_t**2) From a3cbdbe9c412644d898eb3f0fefd215ed85e045b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20H=C3=B6lscher?= Date: Mon, 2 Feb 2026 12:23:09 +0100 Subject: [PATCH 18/22] Update pyproject.toml --- pyproject.toml | 43 ++-- uv.lock | 640 ++++++++++++++++++++++++++++--------------------- 2 files changed, 393 insertions(+), 290 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 507e384..b53f27c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ dynamic = ["version"] authors = [ { name = "Jonas Hoelscher", email = "hoelscher@emt.uni-paderborn.de" } ] -license-files = ["LICENSE"] +license = {file = "LICENSE"} readme = "README.md" classifiers = [ "Developement Status :: 3 - Alpha", @@ -15,12 +15,27 @@ classifiers = [ ] requires-python = ">=3.10" dependencies = [ - "numpy", - "matplotlib", - "scipy>=1.12.0", - "gmsh==4.13.1", - "python-dotenv", - "pyyaml", + "gmsh>=4.13.1", + "matplotlib>=3.10.3", + "numpy>=2.2.6", + "python-dotenv>=1.1.0", + "pyyaml>=6.0.2", + "scipy>=1.15.3", +] + +[project.optional-dependencies] +docs = [ + "sphinx>=8.1.3", + "sphinx-rtd-theme>=3.1.0", +] +lint = [ + "ruff>=0.14.14", +] +test = [ + "pytest>=9.0.2", +] +tests = [ + "pytest>=9.0.2", ] [proect.urls] @@ -31,18 +46,6 @@ requires = ["setuptools>=41", "wheel", "setuptools_scm"] build-backend = "setuptools.build_meta" [tool.setuptools.packages.find] -where = ["."] include = ["plutho"] -[dependency-groups] -test = [ - "pytest" -] -lint = [ - "ruff" -] -ruff = [ - "setuptools_scm", - "sphinx", - "sphinx-rtd-theme" -] +[tool.setuptools_scm] diff --git a/uv.lock b/uv.lock index cc36d56..7b7f524 100644 --- a/uv.lock +++ b/uv.lock @@ -2,7 +2,8 @@ version = 1 revision = 3 requires-python = ">=3.10" resolution-markers = [ - "python_full_version >= '3.11'", + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", "python_full_version < '3.11'", ] @@ -17,81 +18,109 @@ wheels = [ [[package]] name = "babel" -version = "2.17.0" +version = "2.18.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7d/6b/d52e42361e1aa00709585ecc30b3f9684b3ab62530771402248b1b1d6240/babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d", size = 9951852, upload-time = "2025-02-01T15:17:41.026Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/b2/51899539b6ceeeb420d40ed3cd4b7a40519404f9baf3d4ac99dc413a834b/babel-2.18.0.tar.gz", hash = "sha256:b80b99a14bd085fcacfa15c9165f651fbb3406e66cc603abf11c5750937c992d", size = 9959554, upload-time = "2026-02-01T12:30:56.078Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2", size = 10182537, upload-time = "2025-02-01T15:17:37.39Z" }, + { url = "https://files.pythonhosted.org/packages/77/f5/21d2de20e8b8b0408f0681956ca2c69f1320a3848ac50e6e7f39c6159675/babel-2.18.0-py3-none-any.whl", hash = "sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35", size = 10196845, upload-time = "2026-02-01T12:30:53.445Z" }, ] [[package]] name = "certifi" -version = "2025.6.15" +version = "2026.1.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/73/f7/f14b46d4bcd21092d7d3ccef689615220d8a08fb25e564b65d20738e672e/certifi-2025.6.15.tar.gz", hash = "sha256:d747aa5a8b9bbbb1bb8c22bb13e22bd1f18e9796defa16bab421f7f7a317323b", size = 158753, upload-time = "2025-06-15T02:45:51.329Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/2d/a891ca51311197f6ad14a7ef42e2399f36cf2f9bd44752b3dc4eab60fdc5/certifi-2026.1.4.tar.gz", hash = "sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120", size = 154268, upload-time = "2026-01-04T02:42:41.825Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/84/ae/320161bd181fc06471eed047ecce67b693fd7515b16d495d8932db763426/certifi-2025.6.15-py3-none-any.whl", hash = "sha256:2e0c7ce7cb5d8f8634ca55d2ba7e6ec2689a2fd6537d8dec1296a477a4910057", size = 157650, upload-time = "2025-06-15T02:45:49.977Z" }, + { url = "https://files.pythonhosted.org/packages/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-py3-none-any.whl", hash = "sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c", size = 152900, upload-time = "2026-01-04T02:42:40.15Z" }, ] [[package]] name = "charset-normalizer" -version = "3.4.2" +version = "3.4.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e4/33/89c2ced2b67d1c2a61c19c6751aa8902d46ce3dacb23600a283619f5a12d/charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63", size = 126367, upload-time = "2025-05-02T08:34:42.01Z" } +sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/95/28/9901804da60055b406e1a1c5ba7aac1276fb77f1dde635aabfc7fd84b8ab/charset_normalizer-3.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941", size = 201818, upload-time = "2025-05-02T08:31:46.725Z" }, - { url = "https://files.pythonhosted.org/packages/d9/9b/892a8c8af9110935e5adcbb06d9c6fe741b6bb02608c6513983048ba1a18/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd", size = 144649, upload-time = "2025-05-02T08:31:48.889Z" }, - { url = "https://files.pythonhosted.org/packages/7b/a5/4179abd063ff6414223575e008593861d62abfc22455b5d1a44995b7c101/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9cbfacf36cb0ec2897ce0ebc5d08ca44213af24265bd56eca54bee7923c48fd6", size = 155045, upload-time = "2025-05-02T08:31:50.757Z" }, - { url = "https://files.pythonhosted.org/packages/3b/95/bc08c7dfeddd26b4be8c8287b9bb055716f31077c8b0ea1cd09553794665/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18dd2e350387c87dabe711b86f83c9c78af772c748904d372ade190b5c7c9d4d", size = 147356, upload-time = "2025-05-02T08:31:52.634Z" }, - { url = "https://files.pythonhosted.org/packages/a8/2d/7a5b635aa65284bf3eab7653e8b4151ab420ecbae918d3e359d1947b4d61/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8075c35cd58273fee266c58c0c9b670947c19df5fb98e7b66710e04ad4e9ff86", size = 149471, upload-time = "2025-05-02T08:31:56.207Z" }, - { url = "https://files.pythonhosted.org/packages/ae/38/51fc6ac74251fd331a8cfdb7ec57beba8c23fd5493f1050f71c87ef77ed0/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5bf4545e3b962767e5c06fe1738f951f77d27967cb2caa64c28be7c4563e162c", size = 151317, upload-time = "2025-05-02T08:31:57.613Z" }, - { url = "https://files.pythonhosted.org/packages/b7/17/edee1e32215ee6e9e46c3e482645b46575a44a2d72c7dfd49e49f60ce6bf/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a6ab32f7210554a96cd9e33abe3ddd86732beeafc7a28e9955cdf22ffadbab0", size = 146368, upload-time = "2025-05-02T08:31:59.468Z" }, - { url = "https://files.pythonhosted.org/packages/26/2c/ea3e66f2b5f21fd00b2825c94cafb8c326ea6240cd80a91eb09e4a285830/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b33de11b92e9f75a2b545d6e9b6f37e398d86c3e9e9653c4864eb7e89c5773ef", size = 154491, upload-time = "2025-05-02T08:32:01.219Z" }, - { url = "https://files.pythonhosted.org/packages/52/47/7be7fa972422ad062e909fd62460d45c3ef4c141805b7078dbab15904ff7/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8755483f3c00d6c9a77f490c17e6ab0c8729e39e6390328e42521ef175380ae6", size = 157695, upload-time = "2025-05-02T08:32:03.045Z" }, - { url = "https://files.pythonhosted.org/packages/2f/42/9f02c194da282b2b340f28e5fb60762de1151387a36842a92b533685c61e/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:68a328e5f55ec37c57f19ebb1fdc56a248db2e3e9ad769919a58672958e8f366", size = 154849, upload-time = "2025-05-02T08:32:04.651Z" }, - { url = "https://files.pythonhosted.org/packages/67/44/89cacd6628f31fb0b63201a618049be4be2a7435a31b55b5eb1c3674547a/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:21b2899062867b0e1fde9b724f8aecb1af14f2778d69aacd1a5a1853a597a5db", size = 150091, upload-time = "2025-05-02T08:32:06.719Z" }, - { url = "https://files.pythonhosted.org/packages/1f/79/4b8da9f712bc079c0f16b6d67b099b0b8d808c2292c937f267d816ec5ecc/charset_normalizer-3.4.2-cp310-cp310-win32.whl", hash = "sha256:e8082b26888e2f8b36a042a58307d5b917ef2b1cacab921ad3323ef91901c71a", size = 98445, upload-time = "2025-05-02T08:32:08.66Z" }, - { url = "https://files.pythonhosted.org/packages/7d/d7/96970afb4fb66497a40761cdf7bd4f6fca0fc7bafde3a84f836c1f57a926/charset_normalizer-3.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:f69a27e45c43520f5487f27627059b64aaf160415589230992cec34c5e18a509", size = 105782, upload-time = "2025-05-02T08:32:10.46Z" }, - { url = "https://files.pythonhosted.org/packages/05/85/4c40d00dcc6284a1c1ad5de5e0996b06f39d8232f1031cd23c2f5c07ee86/charset_normalizer-3.4.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2", size = 198794, upload-time = "2025-05-02T08:32:11.945Z" }, - { url = "https://files.pythonhosted.org/packages/41/d9/7a6c0b9db952598e97e93cbdfcb91bacd89b9b88c7c983250a77c008703c/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645", size = 142846, upload-time = "2025-05-02T08:32:13.946Z" }, - { url = "https://files.pythonhosted.org/packages/66/82/a37989cda2ace7e37f36c1a8ed16c58cf48965a79c2142713244bf945c89/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd", size = 153350, upload-time = "2025-05-02T08:32:15.873Z" }, - { url = "https://files.pythonhosted.org/packages/df/68/a576b31b694d07b53807269d05ec3f6f1093e9545e8607121995ba7a8313/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8", size = 145657, upload-time = "2025-05-02T08:32:17.283Z" }, - { url = "https://files.pythonhosted.org/packages/92/9b/ad67f03d74554bed3aefd56fe836e1623a50780f7c998d00ca128924a499/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f", size = 147260, upload-time = "2025-05-02T08:32:18.807Z" }, - { url = "https://files.pythonhosted.org/packages/a6/e6/8aebae25e328160b20e31a7e9929b1578bbdc7f42e66f46595a432f8539e/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7", size = 149164, upload-time = "2025-05-02T08:32:20.333Z" }, - { url = "https://files.pythonhosted.org/packages/8b/f2/b3c2f07dbcc248805f10e67a0262c93308cfa149a4cd3d1fe01f593e5fd2/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9", size = 144571, upload-time = "2025-05-02T08:32:21.86Z" }, - { url = "https://files.pythonhosted.org/packages/60/5b/c3f3a94bc345bc211622ea59b4bed9ae63c00920e2e8f11824aa5708e8b7/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544", size = 151952, upload-time = "2025-05-02T08:32:23.434Z" }, - { url = "https://files.pythonhosted.org/packages/e2/4d/ff460c8b474122334c2fa394a3f99a04cf11c646da895f81402ae54f5c42/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82", size = 155959, upload-time = "2025-05-02T08:32:24.993Z" }, - { url = "https://files.pythonhosted.org/packages/a2/2b/b964c6a2fda88611a1fe3d4c400d39c66a42d6c169c924818c848f922415/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0", size = 153030, upload-time = "2025-05-02T08:32:26.435Z" }, - { url = "https://files.pythonhosted.org/packages/59/2e/d3b9811db26a5ebf444bc0fa4f4be5aa6d76fc6e1c0fd537b16c14e849b6/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5", size = 148015, upload-time = "2025-05-02T08:32:28.376Z" }, - { url = "https://files.pythonhosted.org/packages/90/07/c5fd7c11eafd561bb51220d600a788f1c8d77c5eef37ee49454cc5c35575/charset_normalizer-3.4.2-cp311-cp311-win32.whl", hash = "sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a", size = 98106, upload-time = "2025-05-02T08:32:30.281Z" }, - { url = "https://files.pythonhosted.org/packages/a8/05/5e33dbef7e2f773d672b6d79f10ec633d4a71cd96db6673625838a4fd532/charset_normalizer-3.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28", size = 105402, upload-time = "2025-05-02T08:32:32.191Z" }, - { url = "https://files.pythonhosted.org/packages/d7/a4/37f4d6035c89cac7930395a35cc0f1b872e652eaafb76a6075943754f095/charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7", size = 199936, upload-time = "2025-05-02T08:32:33.712Z" }, - { url = "https://files.pythonhosted.org/packages/ee/8a/1a5e33b73e0d9287274f899d967907cd0bf9c343e651755d9307e0dbf2b3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3", size = 143790, upload-time = "2025-05-02T08:32:35.768Z" }, - { url = "https://files.pythonhosted.org/packages/66/52/59521f1d8e6ab1482164fa21409c5ef44da3e9f653c13ba71becdd98dec3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a", size = 153924, upload-time = "2025-05-02T08:32:37.284Z" }, - { url = "https://files.pythonhosted.org/packages/86/2d/fb55fdf41964ec782febbf33cb64be480a6b8f16ded2dbe8db27a405c09f/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214", size = 146626, upload-time = "2025-05-02T08:32:38.803Z" }, - { url = "https://files.pythonhosted.org/packages/8c/73/6ede2ec59bce19b3edf4209d70004253ec5f4e319f9a2e3f2f15601ed5f7/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a", size = 148567, upload-time = "2025-05-02T08:32:40.251Z" }, - { url = "https://files.pythonhosted.org/packages/09/14/957d03c6dc343c04904530b6bef4e5efae5ec7d7990a7cbb868e4595ee30/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd", size = 150957, upload-time = "2025-05-02T08:32:41.705Z" }, - { url = "https://files.pythonhosted.org/packages/0d/c8/8174d0e5c10ccebdcb1b53cc959591c4c722a3ad92461a273e86b9f5a302/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981", size = 145408, upload-time = "2025-05-02T08:32:43.709Z" }, - { url = "https://files.pythonhosted.org/packages/58/aa/8904b84bc8084ac19dc52feb4f5952c6df03ffb460a887b42615ee1382e8/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c", size = 153399, upload-time = "2025-05-02T08:32:46.197Z" }, - { url = "https://files.pythonhosted.org/packages/c2/26/89ee1f0e264d201cb65cf054aca6038c03b1a0c6b4ae998070392a3ce605/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b", size = 156815, upload-time = "2025-05-02T08:32:48.105Z" }, - { url = "https://files.pythonhosted.org/packages/fd/07/68e95b4b345bad3dbbd3a8681737b4338ff2c9df29856a6d6d23ac4c73cb/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d", size = 154537, upload-time = "2025-05-02T08:32:49.719Z" }, - { url = "https://files.pythonhosted.org/packages/77/1a/5eefc0ce04affb98af07bc05f3bac9094513c0e23b0562d64af46a06aae4/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f", size = 149565, upload-time = "2025-05-02T08:32:51.404Z" }, - { url = "https://files.pythonhosted.org/packages/37/a0/2410e5e6032a174c95e0806b1a6585eb21e12f445ebe239fac441995226a/charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c", size = 98357, upload-time = "2025-05-02T08:32:53.079Z" }, - { url = "https://files.pythonhosted.org/packages/6c/4f/c02d5c493967af3eda9c771ad4d2bbc8df6f99ddbeb37ceea6e8716a32bc/charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e", size = 105776, upload-time = "2025-05-02T08:32:54.573Z" }, - { url = "https://files.pythonhosted.org/packages/ea/12/a93df3366ed32db1d907d7593a94f1fe6293903e3e92967bebd6950ed12c/charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0", size = 199622, upload-time = "2025-05-02T08:32:56.363Z" }, - { url = "https://files.pythonhosted.org/packages/04/93/bf204e6f344c39d9937d3c13c8cd5bbfc266472e51fc8c07cb7f64fcd2de/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf", size = 143435, upload-time = "2025-05-02T08:32:58.551Z" }, - { url = "https://files.pythonhosted.org/packages/22/2a/ea8a2095b0bafa6c5b5a55ffdc2f924455233ee7b91c69b7edfcc9e02284/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e", size = 153653, upload-time = "2025-05-02T08:33:00.342Z" }, - { url = "https://files.pythonhosted.org/packages/b6/57/1b090ff183d13cef485dfbe272e2fe57622a76694061353c59da52c9a659/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1", size = 146231, upload-time = "2025-05-02T08:33:02.081Z" }, - { url = "https://files.pythonhosted.org/packages/e2/28/ffc026b26f441fc67bd21ab7f03b313ab3fe46714a14b516f931abe1a2d8/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c", size = 148243, upload-time = "2025-05-02T08:33:04.063Z" }, - { url = "https://files.pythonhosted.org/packages/c0/0f/9abe9bd191629c33e69e47c6ef45ef99773320e9ad8e9cb08b8ab4a8d4cb/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691", size = 150442, upload-time = "2025-05-02T08:33:06.418Z" }, - { url = "https://files.pythonhosted.org/packages/67/7c/a123bbcedca91d5916c056407f89a7f5e8fdfce12ba825d7d6b9954a1a3c/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0", size = 145147, upload-time = "2025-05-02T08:33:08.183Z" }, - { url = "https://files.pythonhosted.org/packages/ec/fe/1ac556fa4899d967b83e9893788e86b6af4d83e4726511eaaad035e36595/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b", size = 153057, upload-time = "2025-05-02T08:33:09.986Z" }, - { url = "https://files.pythonhosted.org/packages/2b/ff/acfc0b0a70b19e3e54febdd5301a98b72fa07635e56f24f60502e954c461/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff", size = 156454, upload-time = "2025-05-02T08:33:11.814Z" }, - { url = "https://files.pythonhosted.org/packages/92/08/95b458ce9c740d0645feb0e96cea1f5ec946ea9c580a94adfe0b617f3573/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b", size = 154174, upload-time = "2025-05-02T08:33:13.707Z" }, - { url = "https://files.pythonhosted.org/packages/78/be/8392efc43487ac051eee6c36d5fbd63032d78f7728cb37aebcc98191f1ff/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148", size = 149166, upload-time = "2025-05-02T08:33:15.458Z" }, - { url = "https://files.pythonhosted.org/packages/44/96/392abd49b094d30b91d9fbda6a69519e95802250b777841cf3bda8fe136c/charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7", size = 98064, upload-time = "2025-05-02T08:33:17.06Z" }, - { url = "https://files.pythonhosted.org/packages/e9/b0/0200da600134e001d91851ddc797809e2fe0ea72de90e09bec5a2fbdaccb/charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980", size = 105641, upload-time = "2025-05-02T08:33:18.753Z" }, - { url = "https://files.pythonhosted.org/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626, upload-time = "2025-05-02T08:34:40.053Z" }, + { url = "https://files.pythonhosted.org/packages/1f/b8/6d51fc1d52cbd52cd4ccedd5b5b2f0f6a11bbf6765c782298b0f3e808541/charset_normalizer-3.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d", size = 209709, upload-time = "2025-10-14T04:40:11.385Z" }, + { url = "https://files.pythonhosted.org/packages/5c/af/1f9d7f7faafe2ddfb6f72a2e07a548a629c61ad510fe60f9630309908fef/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8", size = 148814, upload-time = "2025-10-14T04:40:13.135Z" }, + { url = "https://files.pythonhosted.org/packages/79/3d/f2e3ac2bbc056ca0c204298ea4e3d9db9b4afe437812638759db2c976b5f/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad", size = 144467, upload-time = "2025-10-14T04:40:14.728Z" }, + { url = "https://files.pythonhosted.org/packages/ec/85/1bf997003815e60d57de7bd972c57dc6950446a3e4ccac43bc3070721856/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8", size = 162280, upload-time = "2025-10-14T04:40:16.14Z" }, + { url = "https://files.pythonhosted.org/packages/3e/8e/6aa1952f56b192f54921c436b87f2aaf7c7a7c3d0d1a765547d64fd83c13/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d", size = 159454, upload-time = "2025-10-14T04:40:17.567Z" }, + { url = "https://files.pythonhosted.org/packages/36/3b/60cbd1f8e93aa25d1c669c649b7a655b0b5fb4c571858910ea9332678558/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313", size = 153609, upload-time = "2025-10-14T04:40:19.08Z" }, + { url = "https://files.pythonhosted.org/packages/64/91/6a13396948b8fd3c4b4fd5bc74d045f5637d78c9675585e8e9fbe5636554/charset_normalizer-3.4.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e", size = 151849, upload-time = "2025-10-14T04:40:20.607Z" }, + { url = "https://files.pythonhosted.org/packages/b7/7a/59482e28b9981d105691e968c544cc0df3b7d6133152fb3dcdc8f135da7a/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93", size = 151586, upload-time = "2025-10-14T04:40:21.719Z" }, + { url = "https://files.pythonhosted.org/packages/92/59/f64ef6a1c4bdd2baf892b04cd78792ed8684fbc48d4c2afe467d96b4df57/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0", size = 145290, upload-time = "2025-10-14T04:40:23.069Z" }, + { url = "https://files.pythonhosted.org/packages/6b/63/3bf9f279ddfa641ffa1962b0db6a57a9c294361cc2f5fcac997049a00e9c/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84", size = 163663, upload-time = "2025-10-14T04:40:24.17Z" }, + { url = "https://files.pythonhosted.org/packages/ed/09/c9e38fc8fa9e0849b172b581fd9803bdf6e694041127933934184e19f8c3/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e", size = 151964, upload-time = "2025-10-14T04:40:25.368Z" }, + { url = "https://files.pythonhosted.org/packages/d2/d1/d28b747e512d0da79d8b6a1ac18b7ab2ecfd81b2944c4c710e166d8dd09c/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db", size = 161064, upload-time = "2025-10-14T04:40:26.806Z" }, + { url = "https://files.pythonhosted.org/packages/bb/9a/31d62b611d901c3b9e5500c36aab0ff5eb442043fb3a1c254200d3d397d9/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6", size = 155015, upload-time = "2025-10-14T04:40:28.284Z" }, + { url = "https://files.pythonhosted.org/packages/1f/f3/107e008fa2bff0c8b9319584174418e5e5285fef32f79d8ee6a430d0039c/charset_normalizer-3.4.4-cp310-cp310-win32.whl", hash = "sha256:f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f", size = 99792, upload-time = "2025-10-14T04:40:29.613Z" }, + { url = "https://files.pythonhosted.org/packages/eb/66/e396e8a408843337d7315bab30dbf106c38966f1819f123257f5520f8a96/charset_normalizer-3.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d", size = 107198, upload-time = "2025-10-14T04:40:30.644Z" }, + { url = "https://files.pythonhosted.org/packages/b5/58/01b4f815bf0312704c267f2ccb6e5d42bcc7752340cd487bc9f8c3710597/charset_normalizer-3.4.4-cp310-cp310-win_arm64.whl", hash = "sha256:cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69", size = 100262, upload-time = "2025-10-14T04:40:32.108Z" }, + { url = "https://files.pythonhosted.org/packages/ed/27/c6491ff4954e58a10f69ad90aca8a1b6fe9c5d3c6f380907af3c37435b59/charset_normalizer-3.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8", size = 206988, upload-time = "2025-10-14T04:40:33.79Z" }, + { url = "https://files.pythonhosted.org/packages/94/59/2e87300fe67ab820b5428580a53cad894272dbb97f38a7a814a2a1ac1011/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0", size = 147324, upload-time = "2025-10-14T04:40:34.961Z" }, + { url = "https://files.pythonhosted.org/packages/07/fb/0cf61dc84b2b088391830f6274cb57c82e4da8bbc2efeac8c025edb88772/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3", size = 142742, upload-time = "2025-10-14T04:40:36.105Z" }, + { url = "https://files.pythonhosted.org/packages/62/8b/171935adf2312cd745d290ed93cf16cf0dfe320863ab7cbeeae1dcd6535f/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc", size = 160863, upload-time = "2025-10-14T04:40:37.188Z" }, + { url = "https://files.pythonhosted.org/packages/09/73/ad875b192bda14f2173bfc1bc9a55e009808484a4b256748d931b6948442/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897", size = 157837, upload-time = "2025-10-14T04:40:38.435Z" }, + { url = "https://files.pythonhosted.org/packages/6d/fc/de9cce525b2c5b94b47c70a4b4fb19f871b24995c728e957ee68ab1671ea/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381", size = 151550, upload-time = "2025-10-14T04:40:40.053Z" }, + { url = "https://files.pythonhosted.org/packages/55/c2/43edd615fdfba8c6f2dfbd459b25a6b3b551f24ea21981e23fb768503ce1/charset_normalizer-3.4.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815", size = 149162, upload-time = "2025-10-14T04:40:41.163Z" }, + { url = "https://files.pythonhosted.org/packages/03/86/bde4ad8b4d0e9429a4e82c1e8f5c659993a9a863ad62c7df05cf7b678d75/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0", size = 150019, upload-time = "2025-10-14T04:40:42.276Z" }, + { url = "https://files.pythonhosted.org/packages/1f/86/a151eb2af293a7e7bac3a739b81072585ce36ccfb4493039f49f1d3cae8c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161", size = 143310, upload-time = "2025-10-14T04:40:43.439Z" }, + { url = "https://files.pythonhosted.org/packages/b5/fe/43dae6144a7e07b87478fdfc4dbe9efd5defb0e7ec29f5f58a55aeef7bf7/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4", size = 162022, upload-time = "2025-10-14T04:40:44.547Z" }, + { url = "https://files.pythonhosted.org/packages/80/e6/7aab83774f5d2bca81f42ac58d04caf44f0cc2b65fc6db2b3b2e8a05f3b3/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89", size = 149383, upload-time = "2025-10-14T04:40:46.018Z" }, + { url = "https://files.pythonhosted.org/packages/4f/e8/b289173b4edae05c0dde07f69f8db476a0b511eac556dfe0d6bda3c43384/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569", size = 159098, upload-time = "2025-10-14T04:40:47.081Z" }, + { url = "https://files.pythonhosted.org/packages/d8/df/fe699727754cae3f8478493c7f45f777b17c3ef0600e28abfec8619eb49c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224", size = 152991, upload-time = "2025-10-14T04:40:48.246Z" }, + { url = "https://files.pythonhosted.org/packages/1a/86/584869fe4ddb6ffa3bd9f491b87a01568797fb9bd8933f557dba9771beaf/charset_normalizer-3.4.4-cp311-cp311-win32.whl", hash = "sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a", size = 99456, upload-time = "2025-10-14T04:40:49.376Z" }, + { url = "https://files.pythonhosted.org/packages/65/f6/62fdd5feb60530f50f7e38b4f6a1d5203f4d16ff4f9f0952962c044e919a/charset_normalizer-3.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016", size = 106978, upload-time = "2025-10-14T04:40:50.844Z" }, + { url = "https://files.pythonhosted.org/packages/7a/9d/0710916e6c82948b3be62d9d398cb4fcf4e97b56d6a6aeccd66c4b2f2bd5/charset_normalizer-3.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1", size = 99969, upload-time = "2025-10-14T04:40:52.272Z" }, + { url = "https://files.pythonhosted.org/packages/f3/85/1637cd4af66fa687396e757dec650f28025f2a2f5a5531a3208dc0ec43f2/charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394", size = 208425, upload-time = "2025-10-14T04:40:53.353Z" }, + { url = "https://files.pythonhosted.org/packages/9d/6a/04130023fef2a0d9c62d0bae2649b69f7b7d8d24ea5536feef50551029df/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25", size = 148162, upload-time = "2025-10-14T04:40:54.558Z" }, + { url = "https://files.pythonhosted.org/packages/78/29/62328d79aa60da22c9e0b9a66539feae06ca0f5a4171ac4f7dc285b83688/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef", size = 144558, upload-time = "2025-10-14T04:40:55.677Z" }, + { url = "https://files.pythonhosted.org/packages/86/bb/b32194a4bf15b88403537c2e120b817c61cd4ecffa9b6876e941c3ee38fe/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d", size = 161497, upload-time = "2025-10-14T04:40:57.217Z" }, + { url = "https://files.pythonhosted.org/packages/19/89/a54c82b253d5b9b111dc74aca196ba5ccfcca8242d0fb64146d4d3183ff1/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8", size = 159240, upload-time = "2025-10-14T04:40:58.358Z" }, + { url = "https://files.pythonhosted.org/packages/c0/10/d20b513afe03acc89ec33948320a5544d31f21b05368436d580dec4e234d/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86", size = 153471, upload-time = "2025-10-14T04:40:59.468Z" }, + { url = "https://files.pythonhosted.org/packages/61/fa/fbf177b55bdd727010f9c0a3c49eefa1d10f960e5f09d1d887bf93c2e698/charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a", size = 150864, upload-time = "2025-10-14T04:41:00.623Z" }, + { url = "https://files.pythonhosted.org/packages/05/12/9fbc6a4d39c0198adeebbde20b619790e9236557ca59fc40e0e3cebe6f40/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f", size = 150647, upload-time = "2025-10-14T04:41:01.754Z" }, + { url = "https://files.pythonhosted.org/packages/ad/1f/6a9a593d52e3e8c5d2b167daf8c6b968808efb57ef4c210acb907c365bc4/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc", size = 145110, upload-time = "2025-10-14T04:41:03.231Z" }, + { url = "https://files.pythonhosted.org/packages/30/42/9a52c609e72471b0fc54386dc63c3781a387bb4fe61c20231a4ebcd58bdd/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf", size = 162839, upload-time = "2025-10-14T04:41:04.715Z" }, + { url = "https://files.pythonhosted.org/packages/c4/5b/c0682bbf9f11597073052628ddd38344a3d673fda35a36773f7d19344b23/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15", size = 150667, upload-time = "2025-10-14T04:41:05.827Z" }, + { url = "https://files.pythonhosted.org/packages/e4/24/a41afeab6f990cf2daf6cb8c67419b63b48cf518e4f56022230840c9bfb2/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9", size = 160535, upload-time = "2025-10-14T04:41:06.938Z" }, + { url = "https://files.pythonhosted.org/packages/2a/e5/6a4ce77ed243c4a50a1fecca6aaaab419628c818a49434be428fe24c9957/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0", size = 154816, upload-time = "2025-10-14T04:41:08.101Z" }, + { url = "https://files.pythonhosted.org/packages/a8/ef/89297262b8092b312d29cdb2517cb1237e51db8ecef2e9af5edbe7b683b1/charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26", size = 99694, upload-time = "2025-10-14T04:41:09.23Z" }, + { url = "https://files.pythonhosted.org/packages/3d/2d/1e5ed9dd3b3803994c155cd9aacb60c82c331bad84daf75bcb9c91b3295e/charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525", size = 107131, upload-time = "2025-10-14T04:41:10.467Z" }, + { url = "https://files.pythonhosted.org/packages/d0/d9/0ed4c7098a861482a7b6a95603edce4c0d9db2311af23da1fb2b75ec26fc/charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3", size = 100390, upload-time = "2025-10-14T04:41:11.915Z" }, + { url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091, upload-time = "2025-10-14T04:41:13.346Z" }, + { url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936, upload-time = "2025-10-14T04:41:14.461Z" }, + { url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180, upload-time = "2025-10-14T04:41:15.588Z" }, + { url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346, upload-time = "2025-10-14T04:41:16.738Z" }, + { url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874, upload-time = "2025-10-14T04:41:17.923Z" }, + { url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076, upload-time = "2025-10-14T04:41:19.106Z" }, + { url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601, upload-time = "2025-10-14T04:41:20.245Z" }, + { url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376, upload-time = "2025-10-14T04:41:21.398Z" }, + { url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825, upload-time = "2025-10-14T04:41:22.583Z" }, + { url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583, upload-time = "2025-10-14T04:41:23.754Z" }, + { url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366, upload-time = "2025-10-14T04:41:25.27Z" }, + { url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300, upload-time = "2025-10-14T04:41:26.725Z" }, + { url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465, upload-time = "2025-10-14T04:41:28.322Z" }, + { url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404, upload-time = "2025-10-14T04:41:29.95Z" }, + { url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092, upload-time = "2025-10-14T04:41:31.188Z" }, + { url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408, upload-time = "2025-10-14T04:41:32.624Z" }, + { url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746, upload-time = "2025-10-14T04:41:33.773Z" }, + { url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889, upload-time = "2025-10-14T04:41:34.897Z" }, + { url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641, upload-time = "2025-10-14T04:41:36.116Z" }, + { url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779, upload-time = "2025-10-14T04:41:37.229Z" }, + { url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035, upload-time = "2025-10-14T04:41:38.368Z" }, + { url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542, upload-time = "2025-10-14T04:41:39.862Z" }, + { url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524, upload-time = "2025-10-14T04:41:41.319Z" }, + { url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395, upload-time = "2025-10-14T04:41:42.539Z" }, + { url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680, upload-time = "2025-10-14T04:41:43.661Z" }, + { url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045, upload-time = "2025-10-14T04:41:44.821Z" }, + { url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687, upload-time = "2025-10-14T04:41:46.442Z" }, + { url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014, upload-time = "2025-10-14T04:41:47.631Z" }, + { url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044, upload-time = "2025-10-14T04:41:48.81Z" }, + { url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940, upload-time = "2025-10-14T04:41:49.946Z" }, + { url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104, upload-time = "2025-10-14T04:41:51.051Z" }, + { url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743, upload-time = "2025-10-14T04:41:52.122Z" }, + { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" }, ] [[package]] @@ -184,21 +213,37 @@ wheels = [ name = "docutils" version = "0.21.2" source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] sdist = { url = "https://files.pythonhosted.org/packages/ae/ed/aefcc8cd0ba62a0560c3c18c33925362d46c6075480bfa4df87b28e169a9/docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f", size = 2204444, upload-time = "2024-04-23T18:57:18.24Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", size = 587408, upload-time = "2024-04-23T18:57:14.835Z" }, ] +[[package]] +name = "docutils" +version = "0.22.4" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/ae/b6/03bb70946330e88ffec97aefd3ea75ba575cb2e762061e0e62a213befee8/docutils-0.22.4.tar.gz", hash = "sha256:4db53b1fde9abecbb74d91230d32ab626d94f6badfc575d6db9194a49df29968", size = 2291750, upload-time = "2025-12-18T19:00:26.443Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/02/10/5da547df7a391dcde17f59520a231527b8571e6f46fc8efb02ccb370ab12/docutils-0.22.4-py3-none-any.whl", hash = "sha256:d0013f540772d1420576855455d050a2180186c91c15779301ac2ccb3eeb68de", size = 633196, upload-time = "2025-12-18T19:00:18.077Z" }, +] + [[package]] name = "exceptiongroup" -version = "1.3.0" +version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749, upload-time = "2025-05-10T17:42:51.123Z" } +sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", size = 16674, upload-time = "2025-05-10T17:42:49.33Z" }, + { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, ] [[package]] @@ -255,11 +300,11 @@ wheels = [ [[package]] name = "idna" -version = "3.10" +version = "3.11" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, + { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, ] [[package]] @@ -273,11 +318,11 @@ wheels = [ [[package]] name = "iniconfig" -version = "2.1.0" +version = "2.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" } +sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" }, + { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, ] [[package]] @@ -381,60 +426,87 @@ wheels = [ [[package]] name = "markupsafe" -version = "3.0.2" +version = "3.0.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537, upload-time = "2024-10-18T15:21:54.129Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/04/90/d08277ce111dd22f77149fd1a5d4653eeb3b3eaacbdfcbae5afb2600eebd/MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8", size = 14357, upload-time = "2024-10-18T15:20:51.44Z" }, - { url = "https://files.pythonhosted.org/packages/04/e1/6e2194baeae0bca1fae6629dc0cbbb968d4d941469cbab11a3872edff374/MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158", size = 12393, upload-time = "2024-10-18T15:20:52.426Z" }, - { url = "https://files.pythonhosted.org/packages/1d/69/35fa85a8ece0a437493dc61ce0bb6d459dcba482c34197e3efc829aa357f/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579", size = 21732, upload-time = "2024-10-18T15:20:53.578Z" }, - { url = "https://files.pythonhosted.org/packages/22/35/137da042dfb4720b638d2937c38a9c2df83fe32d20e8c8f3185dbfef05f7/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d", size = 20866, upload-time = "2024-10-18T15:20:55.06Z" }, - { url = "https://files.pythonhosted.org/packages/29/28/6d029a903727a1b62edb51863232152fd335d602def598dade38996887f0/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb", size = 20964, upload-time = "2024-10-18T15:20:55.906Z" }, - { url = "https://files.pythonhosted.org/packages/cc/cd/07438f95f83e8bc028279909d9c9bd39e24149b0d60053a97b2bc4f8aa51/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b", size = 21977, upload-time = "2024-10-18T15:20:57.189Z" }, - { url = "https://files.pythonhosted.org/packages/29/01/84b57395b4cc062f9c4c55ce0df7d3108ca32397299d9df00fedd9117d3d/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c", size = 21366, upload-time = "2024-10-18T15:20:58.235Z" }, - { url = "https://files.pythonhosted.org/packages/bd/6e/61ebf08d8940553afff20d1fb1ba7294b6f8d279df9fd0c0db911b4bbcfd/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171", size = 21091, upload-time = "2024-10-18T15:20:59.235Z" }, - { url = "https://files.pythonhosted.org/packages/11/23/ffbf53694e8c94ebd1e7e491de185124277964344733c45481f32ede2499/MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50", size = 15065, upload-time = "2024-10-18T15:21:00.307Z" }, - { url = "https://files.pythonhosted.org/packages/44/06/e7175d06dd6e9172d4a69a72592cb3f7a996a9c396eee29082826449bbc3/MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a", size = 15514, upload-time = "2024-10-18T15:21:01.122Z" }, - { url = "https://files.pythonhosted.org/packages/6b/28/bbf83e3f76936960b850435576dd5e67034e200469571be53f69174a2dfd/MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d", size = 14353, upload-time = "2024-10-18T15:21:02.187Z" }, - { url = "https://files.pythonhosted.org/packages/6c/30/316d194b093cde57d448a4c3209f22e3046c5bb2fb0820b118292b334be7/MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93", size = 12392, upload-time = "2024-10-18T15:21:02.941Z" }, - { url = "https://files.pythonhosted.org/packages/f2/96/9cdafba8445d3a53cae530aaf83c38ec64c4d5427d975c974084af5bc5d2/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832", size = 23984, upload-time = "2024-10-18T15:21:03.953Z" }, - { url = "https://files.pythonhosted.org/packages/f1/a4/aefb044a2cd8d7334c8a47d3fb2c9f328ac48cb349468cc31c20b539305f/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84", size = 23120, upload-time = "2024-10-18T15:21:06.495Z" }, - { url = "https://files.pythonhosted.org/packages/8d/21/5e4851379f88f3fad1de30361db501300d4f07bcad047d3cb0449fc51f8c/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca", size = 23032, upload-time = "2024-10-18T15:21:07.295Z" }, - { url = "https://files.pythonhosted.org/packages/00/7b/e92c64e079b2d0d7ddf69899c98842f3f9a60a1ae72657c89ce2655c999d/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798", size = 24057, upload-time = "2024-10-18T15:21:08.073Z" }, - { url = "https://files.pythonhosted.org/packages/f9/ac/46f960ca323037caa0a10662ef97d0a4728e890334fc156b9f9e52bcc4ca/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e", size = 23359, upload-time = "2024-10-18T15:21:09.318Z" }, - { url = "https://files.pythonhosted.org/packages/69/84/83439e16197337b8b14b6a5b9c2105fff81d42c2a7c5b58ac7b62ee2c3b1/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4", size = 23306, upload-time = "2024-10-18T15:21:10.185Z" }, - { url = "https://files.pythonhosted.org/packages/9a/34/a15aa69f01e2181ed8d2b685c0d2f6655d5cca2c4db0ddea775e631918cd/MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d", size = 15094, upload-time = "2024-10-18T15:21:11.005Z" }, - { url = "https://files.pythonhosted.org/packages/da/b8/3a3bd761922d416f3dc5d00bfbed11f66b1ab89a0c2b6e887240a30b0f6b/MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b", size = 15521, upload-time = "2024-10-18T15:21:12.911Z" }, - { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274, upload-time = "2024-10-18T15:21:13.777Z" }, - { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348, upload-time = "2024-10-18T15:21:14.822Z" }, - { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149, upload-time = "2024-10-18T15:21:15.642Z" }, - { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118, upload-time = "2024-10-18T15:21:17.133Z" }, - { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993, upload-time = "2024-10-18T15:21:18.064Z" }, - { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178, upload-time = "2024-10-18T15:21:18.859Z" }, - { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319, upload-time = "2024-10-18T15:21:19.671Z" }, - { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352, upload-time = "2024-10-18T15:21:20.971Z" }, - { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097, upload-time = "2024-10-18T15:21:22.646Z" }, - { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601, upload-time = "2024-10-18T15:21:23.499Z" }, - { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274, upload-time = "2024-10-18T15:21:24.577Z" }, - { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352, upload-time = "2024-10-18T15:21:25.382Z" }, - { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122, upload-time = "2024-10-18T15:21:26.199Z" }, - { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085, upload-time = "2024-10-18T15:21:27.029Z" }, - { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978, upload-time = "2024-10-18T15:21:27.846Z" }, - { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208, upload-time = "2024-10-18T15:21:28.744Z" }, - { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357, upload-time = "2024-10-18T15:21:29.545Z" }, - { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344, upload-time = "2024-10-18T15:21:30.366Z" }, - { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101, upload-time = "2024-10-18T15:21:31.207Z" }, - { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603, upload-time = "2024-10-18T15:21:32.032Z" }, - { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510, upload-time = "2024-10-18T15:21:33.625Z" }, - { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486, upload-time = "2024-10-18T15:21:34.611Z" }, - { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480, upload-time = "2024-10-18T15:21:35.398Z" }, - { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914, upload-time = "2024-10-18T15:21:36.231Z" }, - { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796, upload-time = "2024-10-18T15:21:37.073Z" }, - { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473, upload-time = "2024-10-18T15:21:37.932Z" }, - { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114, upload-time = "2024-10-18T15:21:39.799Z" }, - { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098, upload-time = "2024-10-18T15:21:40.813Z" }, - { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208, upload-time = "2024-10-18T15:21:41.814Z" }, - { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739, upload-time = "2024-10-18T15:21:42.784Z" }, + { url = "https://files.pythonhosted.org/packages/e8/4b/3541d44f3937ba468b75da9eebcae497dcf67adb65caa16760b0a6807ebb/markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559", size = 11631, upload-time = "2025-09-27T18:36:05.558Z" }, + { url = "https://files.pythonhosted.org/packages/98/1b/fbd8eed11021cabd9226c37342fa6ca4e8a98d8188a8d9b66740494960e4/markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419", size = 12057, upload-time = "2025-09-27T18:36:07.165Z" }, + { url = "https://files.pythonhosted.org/packages/40/01/e560d658dc0bb8ab762670ece35281dec7b6c1b33f5fbc09ebb57a185519/markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695", size = 22050, upload-time = "2025-09-27T18:36:08.005Z" }, + { url = "https://files.pythonhosted.org/packages/af/cd/ce6e848bbf2c32314c9b237839119c5a564a59725b53157c856e90937b7a/markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591", size = 20681, upload-time = "2025-09-27T18:36:08.881Z" }, + { url = "https://files.pythonhosted.org/packages/c9/2a/b5c12c809f1c3045c4d580b035a743d12fcde53cf685dbc44660826308da/markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c", size = 20705, upload-time = "2025-09-27T18:36:10.131Z" }, + { url = "https://files.pythonhosted.org/packages/cf/e3/9427a68c82728d0a88c50f890d0fc072a1484de2f3ac1ad0bfc1a7214fd5/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f", size = 21524, upload-time = "2025-09-27T18:36:11.324Z" }, + { url = "https://files.pythonhosted.org/packages/bc/36/23578f29e9e582a4d0278e009b38081dbe363c5e7165113fad546918a232/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6", size = 20282, upload-time = "2025-09-27T18:36:12.573Z" }, + { url = "https://files.pythonhosted.org/packages/56/21/dca11354e756ebd03e036bd8ad58d6d7168c80ce1fe5e75218e4945cbab7/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1", size = 20745, upload-time = "2025-09-27T18:36:13.504Z" }, + { url = "https://files.pythonhosted.org/packages/87/99/faba9369a7ad6e4d10b6a5fbf71fa2a188fe4a593b15f0963b73859a1bbd/markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa", size = 14571, upload-time = "2025-09-27T18:36:14.779Z" }, + { url = "https://files.pythonhosted.org/packages/d6/25/55dc3ab959917602c96985cb1253efaa4ff42f71194bddeb61eb7278b8be/markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8", size = 15056, upload-time = "2025-09-27T18:36:16.125Z" }, + { url = "https://files.pythonhosted.org/packages/d0/9e/0a02226640c255d1da0b8d12e24ac2aa6734da68bff14c05dd53b94a0fc3/markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1", size = 13932, upload-time = "2025-09-27T18:36:17.311Z" }, + { url = "https://files.pythonhosted.org/packages/08/db/fefacb2136439fc8dd20e797950e749aa1f4997ed584c62cfb8ef7c2be0e/markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad", size = 11631, upload-time = "2025-09-27T18:36:18.185Z" }, + { url = "https://files.pythonhosted.org/packages/e1/2e/5898933336b61975ce9dc04decbc0a7f2fee78c30353c5efba7f2d6ff27a/markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a", size = 12058, upload-time = "2025-09-27T18:36:19.444Z" }, + { url = "https://files.pythonhosted.org/packages/1d/09/adf2df3699d87d1d8184038df46a9c80d78c0148492323f4693df54e17bb/markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50", size = 24287, upload-time = "2025-09-27T18:36:20.768Z" }, + { url = "https://files.pythonhosted.org/packages/30/ac/0273f6fcb5f42e314c6d8cd99effae6a5354604d461b8d392b5ec9530a54/markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf", size = 22940, upload-time = "2025-09-27T18:36:22.249Z" }, + { url = "https://files.pythonhosted.org/packages/19/ae/31c1be199ef767124c042c6c3e904da327a2f7f0cd63a0337e1eca2967a8/markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f", size = 21887, upload-time = "2025-09-27T18:36:23.535Z" }, + { url = "https://files.pythonhosted.org/packages/b2/76/7edcab99d5349a4532a459e1fe64f0b0467a3365056ae550d3bcf3f79e1e/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a", size = 23692, upload-time = "2025-09-27T18:36:24.823Z" }, + { url = "https://files.pythonhosted.org/packages/a4/28/6e74cdd26d7514849143d69f0bf2399f929c37dc2b31e6829fd2045b2765/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115", size = 21471, upload-time = "2025-09-27T18:36:25.95Z" }, + { url = "https://files.pythonhosted.org/packages/62/7e/a145f36a5c2945673e590850a6f8014318d5577ed7e5920a4b3448e0865d/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a", size = 22923, upload-time = "2025-09-27T18:36:27.109Z" }, + { url = "https://files.pythonhosted.org/packages/0f/62/d9c46a7f5c9adbeeeda52f5b8d802e1094e9717705a645efc71b0913a0a8/markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19", size = 14572, upload-time = "2025-09-27T18:36:28.045Z" }, + { url = "https://files.pythonhosted.org/packages/83/8a/4414c03d3f891739326e1783338e48fb49781cc915b2e0ee052aa490d586/markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01", size = 15077, upload-time = "2025-09-27T18:36:29.025Z" }, + { url = "https://files.pythonhosted.org/packages/35/73/893072b42e6862f319b5207adc9ae06070f095b358655f077f69a35601f0/markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c", size = 13876, upload-time = "2025-09-27T18:36:29.954Z" }, + { url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615, upload-time = "2025-09-27T18:36:30.854Z" }, + { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020, upload-time = "2025-09-27T18:36:31.971Z" }, + { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332, upload-time = "2025-09-27T18:36:32.813Z" }, + { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947, upload-time = "2025-09-27T18:36:33.86Z" }, + { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962, upload-time = "2025-09-27T18:36:35.099Z" }, + { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760, upload-time = "2025-09-27T18:36:36.001Z" }, + { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529, upload-time = "2025-09-27T18:36:36.906Z" }, + { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015, upload-time = "2025-09-27T18:36:37.868Z" }, + { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540, upload-time = "2025-09-27T18:36:38.761Z" }, + { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105, upload-time = "2025-09-27T18:36:39.701Z" }, + { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906, upload-time = "2025-09-27T18:36:40.689Z" }, + { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload-time = "2025-09-27T18:36:41.777Z" }, + { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload-time = "2025-09-27T18:36:43.257Z" }, + { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload-time = "2025-09-27T18:36:44.508Z" }, + { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload-time = "2025-09-27T18:36:45.385Z" }, + { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990, upload-time = "2025-09-27T18:36:46.916Z" }, + { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload-time = "2025-09-27T18:36:47.884Z" }, + { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588, upload-time = "2025-09-27T18:36:48.82Z" }, + { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload-time = "2025-09-27T18:36:49.797Z" }, + { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload-time = "2025-09-27T18:36:51.584Z" }, + { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload-time = "2025-09-27T18:36:52.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload-time = "2025-09-27T18:36:53.513Z" }, + { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload-time = "2025-09-27T18:36:54.819Z" }, + { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload-time = "2025-09-27T18:36:55.714Z" }, + { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload-time = "2025-09-27T18:36:56.908Z" }, + { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload-time = "2025-09-27T18:36:57.913Z" }, + { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284, upload-time = "2025-09-27T18:36:58.833Z" }, + { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload-time = "2025-09-27T18:36:59.739Z" }, + { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769, upload-time = "2025-09-27T18:37:00.719Z" }, + { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload-time = "2025-09-27T18:37:01.673Z" }, + { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" }, + { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" }, + { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" }, + { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" }, + { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" }, + { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" }, + { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" }, + { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" }, + { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" }, + { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" }, + { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" }, + { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" }, + { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" }, + { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" }, + { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" }, + { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" }, + { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" }, + { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" }, + { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" }, + { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" }, + { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" }, + { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" }, + { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" }, + { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" }, ] [[package]] @@ -560,7 +632,8 @@ name = "numpy" version = "2.3.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.11'", + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", ] sdist = { url = "https://files.pythonhosted.org/packages/f3/db/8e12381333aea300890829a0a36bfa738cac95475d88982d538725143fd9/numpy-2.3.0.tar.gz", hash = "sha256:581f87f9e9e9db2cba2141400e160e9dd644ee248788d6f90636eeb8fd9260a6", size = 20382813, upload-time = "2025-06-07T14:54:32.608Z" } wheels = [ @@ -724,46 +797,46 @@ dependencies = [ { name = "scipy" }, ] -[package.dev-dependencies] -lint = [ - { name = "ruff" }, -] -ruff = [ - { name = "setuptools-scm" }, +[package.optional-dependencies] +docs = [ { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, { name = "sphinx-rtd-theme" }, ] +lint = [ + { name = "ruff" }, +] test = [ { name = "pytest" }, ] +tests = [ + { name = "pytest" }, +] [package.metadata] requires-dist = [ - { name = "gmsh", specifier = "==4.13.1" }, - { name = "matplotlib" }, - { name = "numpy" }, - { name = "python-dotenv" }, - { name = "pyyaml" }, - { name = "scipy", specifier = ">=1.12.0" }, -] - -[package.metadata.requires-dev] -lint = [{ name = "ruff" }] -ruff = [ - { name = "setuptools-scm" }, - { name = "sphinx" }, - { name = "sphinx-rtd-theme" }, -] -test = [{ name = "pytest" }] + { name = "gmsh", specifier = ">=4.13.1" }, + { name = "matplotlib", specifier = ">=3.10.3" }, + { name = "numpy", specifier = ">=2.2.6" }, + { name = "pytest", marker = "extra == 'test'", specifier = ">=9.0.2" }, + { name = "pytest", marker = "extra == 'tests'", specifier = ">=9.0.2" }, + { name = "python-dotenv", specifier = ">=1.1.0" }, + { name = "pyyaml", specifier = ">=6.0.2" }, + { name = "ruff", marker = "extra == 'lint'", specifier = ">=0.14.14" }, + { name = "scipy", specifier = ">=1.15.3" }, + { name = "sphinx", marker = "extra == 'docs'", specifier = ">=8.1.3" }, + { name = "sphinx-rtd-theme", marker = "extra == 'docs'", specifier = ">=3.1.0" }, +] +provides-extras = ["docs", "lint", "test", "tests"] [[package]] name = "pygments" -version = "2.19.1" +version = "2.19.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581, upload-time = "2025-01-06T17:26:30.443Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293, upload-time = "2025-01-06T17:26:25.553Z" }, + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, ] [[package]] @@ -777,7 +850,7 @@ wheels = [ [[package]] name = "pytest" -version = "8.4.1" +version = "9.0.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, @@ -788,9 +861,9 @@ dependencies = [ { name = "pygments" }, { name = "tomli", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/08/ba/45911d754e8eba3d5a841a5ce61a65a685ff1798421ac054f85aa8747dfb/pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c", size = 1517714, upload-time = "2025-06-18T05:48:06.109Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/29/16/c8a903f4c4dffe7a12843191437d7cd8e32751d5de349d45d3fe69544e87/pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7", size = 365474, upload-time = "2025-06-18T05:48:03.955Z" }, + { url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" }, ] [[package]] @@ -860,7 +933,7 @@ wheels = [ [[package]] name = "requests" -version = "2.32.4" +version = "2.32.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, @@ -868,43 +941,44 @@ dependencies = [ { name = "idna" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e1/0a/929373653770d8a0d7ea76c37de6e41f11eb07559b103b1c02cafb3f7cf8/requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422", size = 135258, upload-time = "2025-06-09T16:43:07.34Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/e4/56027c4a6b4ae70ca9de302488c5ca95ad4a39e190093d6c1a8ace08341b/requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c", size = 64847, upload-time = "2025-06-09T16:43:05.728Z" }, + { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, ] [[package]] -name = "roman-numerals-py" -version = "3.1.0" +name = "roman-numerals" +version = "4.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/30/76/48fd56d17c5bdbdf65609abbc67288728a98ed4c02919428d4f52d23b24b/roman_numerals_py-3.1.0.tar.gz", hash = "sha256:be4bf804f083a4ce001b5eb7e3c0862479d10f94c936f6c4e5f250aa5ff5bd2d", size = 9017, upload-time = "2025-02-22T07:34:54.333Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ae/f9/41dc953bbeb056c17d5f7a519f50fdf010bd0553be2d630bc69d1e022703/roman_numerals-4.1.0.tar.gz", hash = "sha256:1af8b147eb1405d5839e78aeb93131690495fe9da5c91856cb33ad55a7f1e5b2", size = 9077, upload-time = "2025-12-17T18:25:34.381Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/53/97/d2cbbaa10c9b826af0e10fdf836e1bf344d9f0abb873ebc34d1f49642d3f/roman_numerals_py-3.1.0-py3-none-any.whl", hash = "sha256:9da2ad2fb670bcf24e81070ceb3be72f6c11c440d73bd579fbeca1e9f330954c", size = 7742, upload-time = "2025-02-22T07:34:52.422Z" }, + { url = "https://files.pythonhosted.org/packages/04/54/6f679c435d28e0a568d8e8a7c0a93a09010818634c3c3907fc98d8983770/roman_numerals-4.1.0-py3-none-any.whl", hash = "sha256:647ba99caddc2cc1e55a51e4360689115551bf4476d90e8162cf8c345fe233c7", size = 7676, upload-time = "2025-12-17T18:25:33.098Z" }, ] [[package]] name = "ruff" -version = "0.12.0" +version = "0.14.14" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/24/90/5255432602c0b196a0da6720f6f76b93eb50baef46d3c9b0025e2f9acbf3/ruff-0.12.0.tar.gz", hash = "sha256:4d047db3662418d4a848a3fdbfaf17488b34b62f527ed6f10cb8afd78135bc5c", size = 4376101, upload-time = "2025-06-17T15:19:26.217Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2e/06/f71e3a86b2df0dfa2d2f72195941cd09b44f87711cb7fa5193732cb9a5fc/ruff-0.14.14.tar.gz", hash = "sha256:2d0f819c9a90205f3a867dbbd0be083bee9912e170fd7d9704cc8ae45824896b", size = 4515732, upload-time = "2026-01-22T22:30:17.527Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/fd/b46bb20e14b11ff49dbc74c61de352e0dc07fb650189513631f6fb5fc69f/ruff-0.12.0-py3-none-linux_armv6l.whl", hash = "sha256:5652a9ecdb308a1754d96a68827755f28d5dfb416b06f60fd9e13f26191a8848", size = 10311554, upload-time = "2025-06-17T15:18:45.792Z" }, - { url = "https://files.pythonhosted.org/packages/e7/d3/021dde5a988fa3e25d2468d1dadeea0ae89dc4bc67d0140c6e68818a12a1/ruff-0.12.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:05ed0c914fabc602fc1f3b42c53aa219e5736cb030cdd85640c32dbc73da74a6", size = 11118435, upload-time = "2025-06-17T15:18:49.064Z" }, - { url = "https://files.pythonhosted.org/packages/07/a2/01a5acf495265c667686ec418f19fd5c32bcc326d4c79ac28824aecd6a32/ruff-0.12.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:07a7aa9b69ac3fcfda3c507916d5d1bca10821fe3797d46bad10f2c6de1edda0", size = 10466010, upload-time = "2025-06-17T15:18:51.341Z" }, - { url = "https://files.pythonhosted.org/packages/4c/57/7caf31dd947d72e7aa06c60ecb19c135cad871a0a8a251723088132ce801/ruff-0.12.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e7731c3eec50af71597243bace7ec6104616ca56dda2b99c89935fe926bdcd48", size = 10661366, upload-time = "2025-06-17T15:18:53.29Z" }, - { url = "https://files.pythonhosted.org/packages/e9/ba/aa393b972a782b4bc9ea121e0e358a18981980856190d7d2b6187f63e03a/ruff-0.12.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:952d0630eae628250ab1c70a7fffb641b03e6b4a2d3f3ec6c1d19b4ab6c6c807", size = 10173492, upload-time = "2025-06-17T15:18:55.262Z" }, - { url = "https://files.pythonhosted.org/packages/d7/50/9349ee777614bc3062fc6b038503a59b2034d09dd259daf8192f56c06720/ruff-0.12.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c021f04ea06966b02614d442e94071781c424ab8e02ec7af2f037b4c1e01cc82", size = 11761739, upload-time = "2025-06-17T15:18:58.906Z" }, - { url = "https://files.pythonhosted.org/packages/04/8f/ad459de67c70ec112e2ba7206841c8f4eb340a03ee6a5cabc159fe558b8e/ruff-0.12.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:7d235618283718ee2fe14db07f954f9b2423700919dc688eacf3f8797a11315c", size = 12537098, upload-time = "2025-06-17T15:19:01.316Z" }, - { url = "https://files.pythonhosted.org/packages/ed/50/15ad9c80ebd3c4819f5bd8883e57329f538704ed57bac680d95cb6627527/ruff-0.12.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0c0758038f81beec8cc52ca22de9685b8ae7f7cc18c013ec2050012862cc9165", size = 12154122, upload-time = "2025-06-17T15:19:03.727Z" }, - { url = "https://files.pythonhosted.org/packages/76/e6/79b91e41bc8cc3e78ee95c87093c6cacfa275c786e53c9b11b9358026b3d/ruff-0.12.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:139b3d28027987b78fc8d6cfb61165447bdf3740e650b7c480744873688808c2", size = 11363374, upload-time = "2025-06-17T15:19:05.875Z" }, - { url = "https://files.pythonhosted.org/packages/db/c3/82b292ff8a561850934549aa9dc39e2c4e783ab3c21debe55a495ddf7827/ruff-0.12.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68853e8517b17bba004152aebd9dd77d5213e503a5f2789395b25f26acac0da4", size = 11587647, upload-time = "2025-06-17T15:19:08.246Z" }, - { url = "https://files.pythonhosted.org/packages/2b/42/d5760d742669f285909de1bbf50289baccb647b53e99b8a3b4f7ce1b2001/ruff-0.12.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:3a9512af224b9ac4757f7010843771da6b2b0935a9e5e76bb407caa901a1a514", size = 10527284, upload-time = "2025-06-17T15:19:10.37Z" }, - { url = "https://files.pythonhosted.org/packages/19/f6/fcee9935f25a8a8bba4adbae62495c39ef281256693962c2159e8b284c5f/ruff-0.12.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b08df3d96db798e5beb488d4df03011874aff919a97dcc2dd8539bb2be5d6a88", size = 10158609, upload-time = "2025-06-17T15:19:12.286Z" }, - { url = "https://files.pythonhosted.org/packages/37/fb/057febf0eea07b9384787bfe197e8b3384aa05faa0d6bd844b94ceb29945/ruff-0.12.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:6a315992297a7435a66259073681bb0d8647a826b7a6de45c6934b2ca3a9ed51", size = 11141462, upload-time = "2025-06-17T15:19:15.195Z" }, - { url = "https://files.pythonhosted.org/packages/10/7c/1be8571011585914b9d23c95b15d07eec2d2303e94a03df58294bc9274d4/ruff-0.12.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:1e55e44e770e061f55a7dbc6e9aed47feea07731d809a3710feda2262d2d4d8a", size = 11641616, upload-time = "2025-06-17T15:19:17.6Z" }, - { url = "https://files.pythonhosted.org/packages/6a/ef/b960ab4818f90ff59e571d03c3f992828d4683561095e80f9ef31f3d58b7/ruff-0.12.0-py3-none-win32.whl", hash = "sha256:7162a4c816f8d1555eb195c46ae0bd819834d2a3f18f98cc63819a7b46f474fb", size = 10525289, upload-time = "2025-06-17T15:19:19.688Z" }, - { url = "https://files.pythonhosted.org/packages/34/93/8b16034d493ef958a500f17cda3496c63a537ce9d5a6479feec9558f1695/ruff-0.12.0-py3-none-win_amd64.whl", hash = "sha256:d00b7a157b8fb6d3827b49d3324da34a1e3f93492c1f97b08e222ad7e9b291e0", size = 11598311, upload-time = "2025-06-17T15:19:21.785Z" }, - { url = "https://files.pythonhosted.org/packages/d0/33/4d3e79e4a84533d6cd526bfb42c020a23256ae5e4265d858bd1287831f7d/ruff-0.12.0-py3-none-win_arm64.whl", hash = "sha256:8cd24580405ad8c1cc64d61725bca091d6b6da7eb3d36f72cc605467069d7e8b", size = 10724946, upload-time = "2025-06-17T15:19:23.952Z" }, + { url = "https://files.pythonhosted.org/packages/d2/89/20a12e97bc6b9f9f68343952da08a8099c57237aef953a56b82711d55edd/ruff-0.14.14-py3-none-linux_armv6l.whl", hash = "sha256:7cfe36b56e8489dee8fbc777c61959f60ec0f1f11817e8f2415f429552846aed", size = 10467650, upload-time = "2026-01-22T22:30:08.578Z" }, + { url = "https://files.pythonhosted.org/packages/a3/b1/c5de3fd2d5a831fcae21beda5e3589c0ba67eec8202e992388e4b17a6040/ruff-0.14.14-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6006a0082336e7920b9573ef8a7f52eec837add1265cc74e04ea8a4368cd704c", size = 10883245, upload-time = "2026-01-22T22:30:04.155Z" }, + { url = "https://files.pythonhosted.org/packages/b8/7c/3c1db59a10e7490f8f6f8559d1db8636cbb13dccebf18686f4e3c9d7c772/ruff-0.14.14-py3-none-macosx_11_0_arm64.whl", hash = "sha256:026c1d25996818f0bf498636686199d9bd0d9d6341c9c2c3b62e2a0198b758de", size = 10231273, upload-time = "2026-01-22T22:30:34.642Z" }, + { url = "https://files.pythonhosted.org/packages/a1/6e/5e0e0d9674be0f8581d1f5e0f0a04761203affce3232c1a1189d0e3b4dad/ruff-0.14.14-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f666445819d31210b71e0a6d1c01e24447a20b85458eea25a25fe8142210ae0e", size = 10585753, upload-time = "2026-01-22T22:30:31.781Z" }, + { url = "https://files.pythonhosted.org/packages/23/09/754ab09f46ff1884d422dc26d59ba18b4e5d355be147721bb2518aa2a014/ruff-0.14.14-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3c0f18b922c6d2ff9a5e6c3ee16259adc513ca775bcf82c67ebab7cbd9da5bc8", size = 10286052, upload-time = "2026-01-22T22:30:24.827Z" }, + { url = "https://files.pythonhosted.org/packages/c8/cc/e71f88dd2a12afb5f50733851729d6b571a7c3a35bfdb16c3035132675a0/ruff-0.14.14-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1629e67489c2dea43e8658c3dba659edbfd87361624b4040d1df04c9740ae906", size = 11043637, upload-time = "2026-01-22T22:30:13.239Z" }, + { url = "https://files.pythonhosted.org/packages/67/b2/397245026352494497dac935d7f00f1468c03a23a0c5db6ad8fc49ca3fb2/ruff-0.14.14-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:27493a2131ea0f899057d49d303e4292b2cae2bb57253c1ed1f256fbcd1da480", size = 12194761, upload-time = "2026-01-22T22:30:22.542Z" }, + { url = "https://files.pythonhosted.org/packages/5b/06/06ef271459f778323112c51b7587ce85230785cd64e91772034ddb88f200/ruff-0.14.14-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:01ff589aab3f5b539e35db38425da31a57521efd1e4ad1ae08fc34dbe30bd7df", size = 12005701, upload-time = "2026-01-22T22:30:20.499Z" }, + { url = "https://files.pythonhosted.org/packages/41/d6/99364514541cf811ccc5ac44362f88df66373e9fec1b9d1c4cc830593fe7/ruff-0.14.14-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1cc12d74eef0f29f51775f5b755913eb523546b88e2d733e1d701fe65144e89b", size = 11282455, upload-time = "2026-01-22T22:29:59.679Z" }, + { url = "https://files.pythonhosted.org/packages/ca/71/37daa46f89475f8582b7762ecd2722492df26421714a33e72ccc9a84d7a5/ruff-0.14.14-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb8481604b7a9e75eff53772496201690ce2687067e038b3cc31aaf16aa0b974", size = 11215882, upload-time = "2026-01-22T22:29:57.032Z" }, + { url = "https://files.pythonhosted.org/packages/2c/10/a31f86169ec91c0705e618443ee74ede0bdd94da0a57b28e72db68b2dbac/ruff-0.14.14-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:14649acb1cf7b5d2d283ebd2f58d56b75836ed8c6f329664fa91cdea19e76e66", size = 11180549, upload-time = "2026-01-22T22:30:27.175Z" }, + { url = "https://files.pythonhosted.org/packages/fd/1e/c723f20536b5163adf79bdd10c5f093414293cdf567eed9bdb7b83940f3f/ruff-0.14.14-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e8058d2145566510790eab4e2fad186002e288dec5e0d343a92fe7b0bc1b3e13", size = 10543416, upload-time = "2026-01-22T22:30:01.964Z" }, + { url = "https://files.pythonhosted.org/packages/3e/34/8a84cea7e42c2d94ba5bde1d7a4fae164d6318f13f933d92da6d7c2041ff/ruff-0.14.14-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:e651e977a79e4c758eb807f0481d673a67ffe53cfa92209781dfa3a996cf8412", size = 10285491, upload-time = "2026-01-22T22:30:29.51Z" }, + { url = "https://files.pythonhosted.org/packages/55/ef/b7c5ea0be82518906c978e365e56a77f8de7678c8bb6651ccfbdc178c29f/ruff-0.14.14-py3-none-musllinux_1_2_i686.whl", hash = "sha256:cc8b22da8d9d6fdd844a68ae937e2a0adf9b16514e9a97cc60355e2d4b219fc3", size = 10733525, upload-time = "2026-01-22T22:30:06.499Z" }, + { url = "https://files.pythonhosted.org/packages/6a/5b/aaf1dfbcc53a2811f6cc0a1759de24e4b03e02ba8762daabd9b6bd8c59e3/ruff-0.14.14-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:16bc890fb4cc9781bb05beb5ab4cd51be9e7cb376bf1dd3580512b24eb3fda2b", size = 11315626, upload-time = "2026-01-22T22:30:36.848Z" }, + { url = "https://files.pythonhosted.org/packages/2c/aa/9f89c719c467dfaf8ad799b9bae0df494513fb21d31a6059cb5870e57e74/ruff-0.14.14-py3-none-win32.whl", hash = "sha256:b530c191970b143375b6a68e6f743800b2b786bbcf03a7965b06c4bf04568167", size = 10502442, upload-time = "2026-01-22T22:30:38.93Z" }, + { url = "https://files.pythonhosted.org/packages/87/44/90fa543014c45560cae1fffc63ea059fb3575ee6e1cb654562197e5d16fb/ruff-0.14.14-py3-none-win_amd64.whl", hash = "sha256:3dde1435e6b6fe5b66506c1dff67a421d0b7f6488d466f651c07f4cab3bf20fd", size = 11630486, upload-time = "2026-01-22T22:30:10.852Z" }, + { url = "https://files.pythonhosted.org/packages/9e/6a/40fee331a52339926a92e17ae748827270b288a35ef4a15c9c8f2ec54715/ruff-0.14.14-py3-none-win_arm64.whl", hash = "sha256:56e6981a98b13a32236a72a8da421d7839221fa308b223b9283312312e5ac76c", size = 10920448, upload-time = "2026-01-22T22:30:15.417Z" }, ] [[package]] @@ -964,29 +1038,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/81/06/0a5e5349474e1cbc5757975b21bd4fad0e72ebf138c5592f191646154e06/scipy-1.15.3-cp313-cp313t-win_amd64.whl", hash = "sha256:76ad1fb5f8752eabf0fa02e4cc0336b4e8f021e2d5f061ed37d6d264db35e3ca", size = 40308097, upload-time = "2025-05-08T16:08:27.627Z" }, ] -[[package]] -name = "setuptools" -version = "80.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/18/5d/3bf57dcd21979b887f014ea83c24ae194cfcd12b9e0fda66b957c69d1fca/setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c", size = 1319958, upload-time = "2025-05-27T00:56:51.443Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", size = 1201486, upload-time = "2025-05-27T00:56:49.664Z" }, -] - -[[package]] -name = "setuptools-scm" -version = "8.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "packaging" }, - { name = "setuptools" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b9/19/7ae64b70b2429c48c3a7a4ed36f50f94687d3bfcd0ae2f152367b6410dff/setuptools_scm-8.3.1.tar.gz", hash = "sha256:3d555e92b75dacd037d32bafdf94f97af51ea29ae8c7b234cf94b7a5bd242a63", size = 78088, upload-time = "2025-04-23T11:53:19.739Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/ac/8f96ba9b4cfe3e4ea201f23f4f97165862395e9331a424ed325ae37024a8/setuptools_scm-8.3.1-py3-none-any.whl", hash = "sha256:332ca0d43791b818b841213e76b1971b7711a960761c5bea5fc5cdb5196fbce3", size = 43935, upload-time = "2025-04-23T11:53:17.922Z" }, -] - [[package]] name = "six" version = "1.17.0" @@ -1016,7 +1067,7 @@ dependencies = [ { name = "alabaster", marker = "python_full_version < '3.11'" }, { name = "babel", marker = "python_full_version < '3.11'" }, { name = "colorama", marker = "python_full_version < '3.11' and sys_platform == 'win32'" }, - { name = "docutils", marker = "python_full_version < '3.11'" }, + { name = "docutils", version = "0.21.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "imagesize", marker = "python_full_version < '3.11'" }, { name = "jinja2", marker = "python_full_version < '3.11'" }, { name = "packaging", marker = "python_full_version < '3.11'" }, @@ -1038,48 +1089,81 @@ wheels = [ [[package]] name = "sphinx" -version = "8.2.3" +version = "9.0.4" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.11'", + "python_full_version == '3.11.*'", ] dependencies = [ - { name = "alabaster", marker = "python_full_version >= '3.11'" }, - { name = "babel", marker = "python_full_version >= '3.11'" }, - { name = "colorama", marker = "python_full_version >= '3.11' and sys_platform == 'win32'" }, - { name = "docutils", marker = "python_full_version >= '3.11'" }, - { name = "imagesize", marker = "python_full_version >= '3.11'" }, - { name = "jinja2", marker = "python_full_version >= '3.11'" }, - { name = "packaging", marker = "python_full_version >= '3.11'" }, - { name = "pygments", marker = "python_full_version >= '3.11'" }, - { name = "requests", marker = "python_full_version >= '3.11'" }, - { name = "roman-numerals-py", marker = "python_full_version >= '3.11'" }, - { name = "snowballstemmer", marker = "python_full_version >= '3.11'" }, - { name = "sphinxcontrib-applehelp", marker = "python_full_version >= '3.11'" }, - { name = "sphinxcontrib-devhelp", marker = "python_full_version >= '3.11'" }, - { name = "sphinxcontrib-htmlhelp", marker = "python_full_version >= '3.11'" }, - { name = "sphinxcontrib-jsmath", marker = "python_full_version >= '3.11'" }, - { name = "sphinxcontrib-qthelp", marker = "python_full_version >= '3.11'" }, - { name = "sphinxcontrib-serializinghtml", marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/38/ad/4360e50ed56cb483667b8e6dadf2d3fda62359593faabbe749a27c4eaca6/sphinx-8.2.3.tar.gz", hash = "sha256:398ad29dee7f63a75888314e9424d40f52ce5a6a87ae88e7071e80af296ec348", size = 8321876, upload-time = "2025-03-02T22:31:59.658Z" } + { name = "alabaster", marker = "python_full_version == '3.11.*'" }, + { name = "babel", marker = "python_full_version == '3.11.*'" }, + { name = "colorama", marker = "python_full_version == '3.11.*' and sys_platform == 'win32'" }, + { name = "docutils", version = "0.22.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "imagesize", marker = "python_full_version == '3.11.*'" }, + { name = "jinja2", marker = "python_full_version == '3.11.*'" }, + { name = "packaging", marker = "python_full_version == '3.11.*'" }, + { name = "pygments", marker = "python_full_version == '3.11.*'" }, + { name = "requests", marker = "python_full_version == '3.11.*'" }, + { name = "roman-numerals", marker = "python_full_version == '3.11.*'" }, + { name = "snowballstemmer", marker = "python_full_version == '3.11.*'" }, + { name = "sphinxcontrib-applehelp", marker = "python_full_version == '3.11.*'" }, + { name = "sphinxcontrib-devhelp", marker = "python_full_version == '3.11.*'" }, + { name = "sphinxcontrib-htmlhelp", marker = "python_full_version == '3.11.*'" }, + { name = "sphinxcontrib-jsmath", marker = "python_full_version == '3.11.*'" }, + { name = "sphinxcontrib-qthelp", marker = "python_full_version == '3.11.*'" }, + { name = "sphinxcontrib-serializinghtml", marker = "python_full_version == '3.11.*'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/50/a8c6ccc36d5eacdfd7913ddccd15a9cee03ecafc5ee2bc40e1f168d85022/sphinx-9.0.4.tar.gz", hash = "sha256:594ef59d042972abbc581d8baa577404abe4e6c3b04ef61bd7fc2acbd51f3fa3", size = 8710502, upload-time = "2025-12-04T07:45:27.343Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/31/53/136e9eca6e0b9dc0e1962e2c908fbea2e5ac000c2a2fbd9a35797958c48b/sphinx-8.2.3-py3-none-any.whl", hash = "sha256:4405915165f13521d875a8c29c8970800a0141c14cc5416a38feca4ea5d9b9c3", size = 3589741, upload-time = "2025-03-02T22:31:56.836Z" }, + { url = "https://files.pythonhosted.org/packages/c6/3f/4bbd76424c393caead2e1eb89777f575dee5c8653e2d4b6afd7a564f5974/sphinx-9.0.4-py3-none-any.whl", hash = "sha256:5bebc595a5e943ea248b99c13814c1c5e10b3ece718976824ffa7959ff95fffb", size = 3917713, upload-time = "2025-12-04T07:45:24.944Z" }, +] + +[[package]] +name = "sphinx" +version = "9.1.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12'", +] +dependencies = [ + { name = "alabaster", marker = "python_full_version >= '3.12'" }, + { name = "babel", marker = "python_full_version >= '3.12'" }, + { name = "colorama", marker = "python_full_version >= '3.12' and sys_platform == 'win32'" }, + { name = "docutils", version = "0.22.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "imagesize", marker = "python_full_version >= '3.12'" }, + { name = "jinja2", marker = "python_full_version >= '3.12'" }, + { name = "packaging", marker = "python_full_version >= '3.12'" }, + { name = "pygments", marker = "python_full_version >= '3.12'" }, + { name = "requests", marker = "python_full_version >= '3.12'" }, + { name = "roman-numerals", marker = "python_full_version >= '3.12'" }, + { name = "snowballstemmer", marker = "python_full_version >= '3.12'" }, + { name = "sphinxcontrib-applehelp", marker = "python_full_version >= '3.12'" }, + { name = "sphinxcontrib-devhelp", marker = "python_full_version >= '3.12'" }, + { name = "sphinxcontrib-htmlhelp", marker = "python_full_version >= '3.12'" }, + { name = "sphinxcontrib-jsmath", marker = "python_full_version >= '3.12'" }, + { name = "sphinxcontrib-qthelp", marker = "python_full_version >= '3.12'" }, + { name = "sphinxcontrib-serializinghtml", marker = "python_full_version >= '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cd/bd/f08eb0f4eed5c83f1ba2a3bd18f7745a2b1525fad70660a1c00224ec468a/sphinx-9.1.0.tar.gz", hash = "sha256:7741722357dd75f8190766926071fed3bdc211c74dd2d7d4df5404da95930ddb", size = 8718324, upload-time = "2025-12-31T15:09:27.646Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/73/f7/b1884cb3188ab181fc81fa00c266699dab600f927a964df02ec3d5d1916a/sphinx-9.1.0-py3-none-any.whl", hash = "sha256:c84fdd4e782504495fe4f2c0b3413d6c2bf388589bb352d439b2a3bb99991978", size = 3921742, upload-time = "2025-12-31T15:09:25.561Z" }, ] [[package]] name = "sphinx-rtd-theme" -version = "3.0.2" +version = "3.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "docutils" }, + { name = "docutils", version = "0.21.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "docutils", version = "0.22.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, { name = "sphinxcontrib-jquery" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/91/44/c97faec644d29a5ceddd3020ae2edffa69e7d00054a8c7a6021e82f20335/sphinx_rtd_theme-3.0.2.tar.gz", hash = "sha256:b7457bc25dda723b20b086a670b9953c859eab60a2a03ee8eb2bb23e176e5f85", size = 7620463, upload-time = "2024-11-13T11:06:04.545Z" } +sdist = { url = "https://files.pythonhosted.org/packages/84/68/a1bfbf38c0f7bccc9b10bbf76b94606f64acb1552ae394f0b8285bfaea25/sphinx_rtd_theme-3.1.0.tar.gz", hash = "sha256:b44276f2c276e909239a4f6c955aa667aaafeb78597923b1c60babc76db78e4c", size = 7620915, upload-time = "2026-01-12T16:03:31.17Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/85/77/46e3bac77b82b4df5bb5b61f2de98637724f246b4966cfc34bc5895d852a/sphinx_rtd_theme-3.0.2-py2.py3-none-any.whl", hash = "sha256:422ccc750c3a3a311de4ae327e82affdaf59eb695ba4936538552f3b00f4ee13", size = 7655561, upload-time = "2024-11-13T11:06:02.094Z" }, + { url = "https://files.pythonhosted.org/packages/87/c7/b5c8015d823bfda1a346adb2c634a2101d50bb75d421eb6dcb31acd25ebc/sphinx_rtd_theme-3.1.0-py2.py3-none-any.whl", hash = "sha256:1785824ae8e6632060490f67cf3a72d404a85d2d9fc26bce3619944de5682b89", size = 7655617, upload-time = "2026-01-12T16:03:28.101Z" }, ] [[package]] @@ -1115,7 +1199,8 @@ version = "4.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/de/f3/aa67467e051df70a6330fe7770894b3e4f09436dea6881ae0b4f3d87cad8/sphinxcontrib-jquery-4.1.tar.gz", hash = "sha256:1620739f04e36a2c779f1a131a2dfd49b2fd07351bf1968ced074365933abc7a", size = 122331, upload-time = "2023-03-14T15:01:01.944Z" } wheels = [ @@ -1151,57 +1236,72 @@ wheels = [ [[package]] name = "tomli" -version = "2.2.1" +version = "2.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175, upload-time = "2024-11-27T22:38:36.873Z" } +sdist = { url = "https://files.pythonhosted.org/packages/82/30/31573e9457673ab10aa432461bee537ce6cef177667deca369efb79df071/tomli-2.4.0.tar.gz", hash = "sha256:aa89c3f6c277dd275d8e243ad24f3b5e701491a860d5121f2cdd399fbb31fc9c", size = 17477, upload-time = "2026-01-11T11:22:38.165Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", size = 131077, upload-time = "2024-11-27T22:37:54.956Z" }, - { url = "https://files.pythonhosted.org/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", size = 123429, upload-time = "2024-11-27T22:37:56.698Z" }, - { url = "https://files.pythonhosted.org/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", size = 226067, upload-time = "2024-11-27T22:37:57.63Z" }, - { url = "https://files.pythonhosted.org/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", size = 236030, upload-time = "2024-11-27T22:37:59.344Z" }, - { url = "https://files.pythonhosted.org/packages/1f/47/999514fa49cfaf7a92c805a86c3c43f4215621855d151b61c602abb38091/tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", size = 240898, upload-time = "2024-11-27T22:38:00.429Z" }, - { url = "https://files.pythonhosted.org/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", size = 229894, upload-time = "2024-11-27T22:38:02.094Z" }, - { url = "https://files.pythonhosted.org/packages/55/18/5d8bc5b0a0362311ce4d18830a5d28943667599a60d20118074ea1b01bb7/tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", size = 245319, upload-time = "2024-11-27T22:38:03.206Z" }, - { url = "https://files.pythonhosted.org/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", size = 238273, upload-time = "2024-11-27T22:38:04.217Z" }, - { url = "https://files.pythonhosted.org/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", size = 98310, upload-time = "2024-11-27T22:38:05.908Z" }, - { url = "https://files.pythonhosted.org/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", size = 108309, upload-time = "2024-11-27T22:38:06.812Z" }, - { url = "https://files.pythonhosted.org/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", size = 132762, upload-time = "2024-11-27T22:38:07.731Z" }, - { url = "https://files.pythonhosted.org/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", size = 123453, upload-time = "2024-11-27T22:38:09.384Z" }, - { url = "https://files.pythonhosted.org/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", size = 233486, upload-time = "2024-11-27T22:38:10.329Z" }, - { url = "https://files.pythonhosted.org/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", size = 242349, upload-time = "2024-11-27T22:38:11.443Z" }, - { url = "https://files.pythonhosted.org/packages/ab/df/bfa89627d13a5cc22402e441e8a931ef2108403db390ff3345c05253935e/tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", size = 252159, upload-time = "2024-11-27T22:38:13.099Z" }, - { url = "https://files.pythonhosted.org/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", size = 237243, upload-time = "2024-11-27T22:38:14.766Z" }, - { url = "https://files.pythonhosted.org/packages/b4/04/885d3b1f650e1153cbb93a6a9782c58a972b94ea4483ae4ac5cedd5e4a09/tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", size = 259645, upload-time = "2024-11-27T22:38:15.843Z" }, - { url = "https://files.pythonhosted.org/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", size = 244584, upload-time = "2024-11-27T22:38:17.645Z" }, - { url = "https://files.pythonhosted.org/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875, upload-time = "2024-11-27T22:38:19.159Z" }, - { url = "https://files.pythonhosted.org/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418, upload-time = "2024-11-27T22:38:20.064Z" }, - { url = "https://files.pythonhosted.org/packages/04/90/2ee5f2e0362cb8a0b6499dc44f4d7d48f8fff06d28ba46e6f1eaa61a1388/tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7", size = 132708, upload-time = "2024-11-27T22:38:21.659Z" }, - { url = "https://files.pythonhosted.org/packages/c0/ec/46b4108816de6b385141f082ba99e315501ccd0a2ea23db4a100dd3990ea/tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", size = 123582, upload-time = "2024-11-27T22:38:22.693Z" }, - { url = "https://files.pythonhosted.org/packages/a0/bd/b470466d0137b37b68d24556c38a0cc819e8febe392d5b199dcd7f578365/tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", size = 232543, upload-time = "2024-11-27T22:38:24.367Z" }, - { url = "https://files.pythonhosted.org/packages/d9/e5/82e80ff3b751373f7cead2815bcbe2d51c895b3c990686741a8e56ec42ab/tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", size = 241691, upload-time = "2024-11-27T22:38:26.081Z" }, - { url = "https://files.pythonhosted.org/packages/05/7e/2a110bc2713557d6a1bfb06af23dd01e7dde52b6ee7dadc589868f9abfac/tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", size = 251170, upload-time = "2024-11-27T22:38:27.921Z" }, - { url = "https://files.pythonhosted.org/packages/64/7b/22d713946efe00e0adbcdfd6d1aa119ae03fd0b60ebed51ebb3fa9f5a2e5/tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", size = 236530, upload-time = "2024-11-27T22:38:29.591Z" }, - { url = "https://files.pythonhosted.org/packages/38/31/3a76f67da4b0cf37b742ca76beaf819dca0ebef26d78fc794a576e08accf/tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", size = 258666, upload-time = "2024-11-27T22:38:30.639Z" }, - { url = "https://files.pythonhosted.org/packages/07/10/5af1293da642aded87e8a988753945d0cf7e00a9452d3911dd3bb354c9e2/tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", size = 243954, upload-time = "2024-11-27T22:38:31.702Z" }, - { url = "https://files.pythonhosted.org/packages/5b/b9/1ed31d167be802da0fc95020d04cd27b7d7065cc6fbefdd2f9186f60d7bd/tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", size = 98724, upload-time = "2024-11-27T22:38:32.837Z" }, - { url = "https://files.pythonhosted.org/packages/c7/32/b0963458706accd9afcfeb867c0f9175a741bf7b19cd424230714d722198/tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", size = 109383, upload-time = "2024-11-27T22:38:34.455Z" }, - { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257, upload-time = "2024-11-27T22:38:35.385Z" }, + { url = "https://files.pythonhosted.org/packages/3c/d9/3dc2289e1f3b32eb19b9785b6a006b28ee99acb37d1d47f78d4c10e28bf8/tomli-2.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b5ef256a3fd497d4973c11bf142e9ed78b150d36f5773f1ca6088c230ffc5867", size = 153663, upload-time = "2026-01-11T11:21:45.27Z" }, + { url = "https://files.pythonhosted.org/packages/51/32/ef9f6845e6b9ca392cd3f64f9ec185cc6f09f0a2df3db08cbe8809d1d435/tomli-2.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5572e41282d5268eb09a697c89a7bee84fae66511f87533a6f88bd2f7b652da9", size = 148469, upload-time = "2026-01-11T11:21:46.873Z" }, + { url = "https://files.pythonhosted.org/packages/d6/c2/506e44cce89a8b1b1e047d64bd495c22c9f71f21e05f380f1a950dd9c217/tomli-2.4.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:551e321c6ba03b55676970b47cb1b73f14a0a4dce6a3e1a9458fd6d921d72e95", size = 236039, upload-time = "2026-01-11T11:21:48.503Z" }, + { url = "https://files.pythonhosted.org/packages/b3/40/e1b65986dbc861b7e986e8ec394598187fa8aee85b1650b01dd925ca0be8/tomli-2.4.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e3f639a7a8f10069d0e15408c0b96a2a828cfdec6fca05296ebcdcc28ca7c76", size = 243007, upload-time = "2026-01-11T11:21:49.456Z" }, + { url = "https://files.pythonhosted.org/packages/9c/6f/6e39ce66b58a5b7ae572a0f4352ff40c71e8573633deda43f6a379d56b3e/tomli-2.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1b168f2731796b045128c45982d3a4874057626da0e2ef1fdd722848b741361d", size = 240875, upload-time = "2026-01-11T11:21:50.755Z" }, + { url = "https://files.pythonhosted.org/packages/aa/ad/cb089cb190487caa80204d503c7fd0f4d443f90b95cf4ef5cf5aa0f439b0/tomli-2.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:133e93646ec4300d651839d382d63edff11d8978be23da4cc106f5a18b7d0576", size = 246271, upload-time = "2026-01-11T11:21:51.81Z" }, + { url = "https://files.pythonhosted.org/packages/0b/63/69125220e47fd7a3a27fd0de0c6398c89432fec41bc739823bcc66506af6/tomli-2.4.0-cp311-cp311-win32.whl", hash = "sha256:b6c78bdf37764092d369722d9946cb65b8767bfa4110f902a1b2542d8d173c8a", size = 96770, upload-time = "2026-01-11T11:21:52.647Z" }, + { url = "https://files.pythonhosted.org/packages/1e/0d/a22bb6c83f83386b0008425a6cd1fa1c14b5f3dd4bad05e98cf3dbbf4a64/tomli-2.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:d3d1654e11d724760cdb37a3d7691f0be9db5fbdaef59c9f532aabf87006dbaa", size = 107626, upload-time = "2026-01-11T11:21:53.459Z" }, + { url = "https://files.pythonhosted.org/packages/2f/6d/77be674a3485e75cacbf2ddba2b146911477bd887dda9d8c9dfb2f15e871/tomli-2.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:cae9c19ed12d4e8f3ebf46d1a75090e4c0dc16271c5bce1c833ac168f08fb614", size = 94842, upload-time = "2026-01-11T11:21:54.831Z" }, + { url = "https://files.pythonhosted.org/packages/3c/43/7389a1869f2f26dba52404e1ef13b4784b6b37dac93bac53457e3ff24ca3/tomli-2.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:920b1de295e72887bafa3ad9f7a792f811847d57ea6b1215154030cf131f16b1", size = 154894, upload-time = "2026-01-11T11:21:56.07Z" }, + { url = "https://files.pythonhosted.org/packages/e9/05/2f9bf110b5294132b2edf13fe6ca6ae456204f3d749f623307cbb7a946f2/tomli-2.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d6d9a4aee98fac3eab4952ad1d73aee87359452d1c086b5ceb43ed02ddb16b8", size = 149053, upload-time = "2026-01-11T11:21:57.467Z" }, + { url = "https://files.pythonhosted.org/packages/e8/41/1eda3ca1abc6f6154a8db4d714a4d35c4ad90adc0bcf700657291593fbf3/tomli-2.4.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:36b9d05b51e65b254ea6c2585b59d2c4cb91c8a3d91d0ed0f17591a29aaea54a", size = 243481, upload-time = "2026-01-11T11:21:58.661Z" }, + { url = "https://files.pythonhosted.org/packages/d2/6d/02ff5ab6c8868b41e7d4b987ce2b5f6a51d3335a70aa144edd999e055a01/tomli-2.4.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c8a885b370751837c029ef9bc014f27d80840e48bac415f3412e6593bbc18c1", size = 251720, upload-time = "2026-01-11T11:22:00.178Z" }, + { url = "https://files.pythonhosted.org/packages/7b/57/0405c59a909c45d5b6f146107c6d997825aa87568b042042f7a9c0afed34/tomli-2.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8768715ffc41f0008abe25d808c20c3d990f42b6e2e58305d5da280ae7d1fa3b", size = 247014, upload-time = "2026-01-11T11:22:01.238Z" }, + { url = "https://files.pythonhosted.org/packages/2c/0e/2e37568edd944b4165735687cbaf2fe3648129e440c26d02223672ee0630/tomli-2.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b438885858efd5be02a9a133caf5812b8776ee0c969fea02c45e8e3f296ba51", size = 251820, upload-time = "2026-01-11T11:22:02.727Z" }, + { url = "https://files.pythonhosted.org/packages/5a/1c/ee3b707fdac82aeeb92d1a113f803cf6d0f37bdca0849cb489553e1f417a/tomli-2.4.0-cp312-cp312-win32.whl", hash = "sha256:0408e3de5ec77cc7f81960c362543cbbd91ef883e3138e81b729fc3eea5b9729", size = 97712, upload-time = "2026-01-11T11:22:03.777Z" }, + { url = "https://files.pythonhosted.org/packages/69/13/c07a9177d0b3bab7913299b9278845fc6eaaca14a02667c6be0b0a2270c8/tomli-2.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:685306e2cc7da35be4ee914fd34ab801a6acacb061b6a7abca922aaf9ad368da", size = 108296, upload-time = "2026-01-11T11:22:04.86Z" }, + { url = "https://files.pythonhosted.org/packages/18/27/e267a60bbeeee343bcc279bb9e8fbed0cbe224bc7b2a3dc2975f22809a09/tomli-2.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:5aa48d7c2356055feef06a43611fc401a07337d5b006be13a30f6c58f869e3c3", size = 94553, upload-time = "2026-01-11T11:22:05.854Z" }, + { url = "https://files.pythonhosted.org/packages/34/91/7f65f9809f2936e1f4ce6268ae1903074563603b2a2bd969ebbda802744f/tomli-2.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84d081fbc252d1b6a982e1870660e7330fb8f90f676f6e78b052ad4e64714bf0", size = 154915, upload-time = "2026-01-11T11:22:06.703Z" }, + { url = "https://files.pythonhosted.org/packages/20/aa/64dd73a5a849c2e8f216b755599c511badde80e91e9bc2271baa7b2cdbb1/tomli-2.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9a08144fa4cba33db5255f9b74f0b89888622109bd2776148f2597447f92a94e", size = 149038, upload-time = "2026-01-11T11:22:07.56Z" }, + { url = "https://files.pythonhosted.org/packages/9e/8a/6d38870bd3d52c8d1505ce054469a73f73a0fe62c0eaf5dddf61447e32fa/tomli-2.4.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c73add4bb52a206fd0c0723432db123c0c75c280cbd67174dd9d2db228ebb1b4", size = 242245, upload-time = "2026-01-11T11:22:08.344Z" }, + { url = "https://files.pythonhosted.org/packages/59/bb/8002fadefb64ab2669e5b977df3f5e444febea60e717e755b38bb7c41029/tomli-2.4.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fb2945cbe303b1419e2706e711b7113da57b7db31ee378d08712d678a34e51e", size = 250335, upload-time = "2026-01-11T11:22:09.951Z" }, + { url = "https://files.pythonhosted.org/packages/a5/3d/4cdb6f791682b2ea916af2de96121b3cb1284d7c203d97d92d6003e91c8d/tomli-2.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bbb1b10aa643d973366dc2cb1ad94f99c1726a02343d43cbc011edbfac579e7c", size = 245962, upload-time = "2026-01-11T11:22:11.27Z" }, + { url = "https://files.pythonhosted.org/packages/f2/4a/5f25789f9a460bd858ba9756ff52d0830d825b458e13f754952dd15fb7bb/tomli-2.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4cbcb367d44a1f0c2be408758b43e1ffb5308abe0ea222897d6bfc8e8281ef2f", size = 250396, upload-time = "2026-01-11T11:22:12.325Z" }, + { url = "https://files.pythonhosted.org/packages/aa/2f/b73a36fea58dfa08e8b3a268750e6853a6aac2a349241a905ebd86f3047a/tomli-2.4.0-cp313-cp313-win32.whl", hash = "sha256:7d49c66a7d5e56ac959cb6fc583aff0651094ec071ba9ad43df785abc2320d86", size = 97530, upload-time = "2026-01-11T11:22:13.865Z" }, + { url = "https://files.pythonhosted.org/packages/3b/af/ca18c134b5d75de7e8dc551c5234eaba2e8e951f6b30139599b53de9c187/tomli-2.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:3cf226acb51d8f1c394c1b310e0e0e61fecdd7adcb78d01e294ac297dd2e7f87", size = 108227, upload-time = "2026-01-11T11:22:15.224Z" }, + { url = "https://files.pythonhosted.org/packages/22/c3/b386b832f209fee8073c8138ec50f27b4460db2fdae9ffe022df89a57f9b/tomli-2.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:d20b797a5c1ad80c516e41bc1fb0443ddb5006e9aaa7bda2d71978346aeb9132", size = 94748, upload-time = "2026-01-11T11:22:16.009Z" }, + { url = "https://files.pythonhosted.org/packages/f3/c4/84047a97eb1004418bc10bdbcfebda209fca6338002eba2dc27cc6d13563/tomli-2.4.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:26ab906a1eb794cd4e103691daa23d95c6919cc2fa9160000ac02370cc9dd3f6", size = 154725, upload-time = "2026-01-11T11:22:17.269Z" }, + { url = "https://files.pythonhosted.org/packages/a8/5d/d39038e646060b9d76274078cddf146ced86dc2b9e8bbf737ad5983609a0/tomli-2.4.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:20cedb4ee43278bc4f2fee6cb50daec836959aadaf948db5172e776dd3d993fc", size = 148901, upload-time = "2026-01-11T11:22:18.287Z" }, + { url = "https://files.pythonhosted.org/packages/73/e5/383be1724cb30f4ce44983d249645684a48c435e1cd4f8b5cded8a816d3c/tomli-2.4.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:39b0b5d1b6dd03684b3fb276407ebed7090bbec989fa55838c98560c01113b66", size = 243375, upload-time = "2026-01-11T11:22:19.154Z" }, + { url = "https://files.pythonhosted.org/packages/31/f0/bea80c17971c8d16d3cc109dc3585b0f2ce1036b5f4a8a183789023574f2/tomli-2.4.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a26d7ff68dfdb9f87a016ecfd1e1c2bacbe3108f4e0f8bcd2228ef9a766c787d", size = 250639, upload-time = "2026-01-11T11:22:20.168Z" }, + { url = "https://files.pythonhosted.org/packages/2c/8f/2853c36abbb7608e3f945d8a74e32ed3a74ee3a1f468f1ffc7d1cb3abba6/tomli-2.4.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:20ffd184fb1df76a66e34bd1b36b4a4641bd2b82954befa32fe8163e79f1a702", size = 246897, upload-time = "2026-01-11T11:22:21.544Z" }, + { url = "https://files.pythonhosted.org/packages/49/f0/6c05e3196ed5337b9fe7ea003e95fd3819a840b7a0f2bf5a408ef1dad8ed/tomli-2.4.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:75c2f8bbddf170e8effc98f5e9084a8751f8174ea6ccf4fca5398436e0320bc8", size = 254697, upload-time = "2026-01-11T11:22:23.058Z" }, + { url = "https://files.pythonhosted.org/packages/f3/f5/2922ef29c9f2951883525def7429967fc4d8208494e5ab524234f06b688b/tomli-2.4.0-cp314-cp314-win32.whl", hash = "sha256:31d556d079d72db7c584c0627ff3a24c5d3fb4f730221d3444f3efb1b2514776", size = 98567, upload-time = "2026-01-11T11:22:24.033Z" }, + { url = "https://files.pythonhosted.org/packages/7b/31/22b52e2e06dd2a5fdbc3ee73226d763b184ff21fc24e20316a44ccc4d96b/tomli-2.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:43e685b9b2341681907759cf3a04e14d7104b3580f808cfde1dfdb60ada85475", size = 108556, upload-time = "2026-01-11T11:22:25.378Z" }, + { url = "https://files.pythonhosted.org/packages/48/3d/5058dff3255a3d01b705413f64f4306a141a8fd7a251e5a495e3f192a998/tomli-2.4.0-cp314-cp314-win_arm64.whl", hash = "sha256:3d895d56bd3f82ddd6faaff993c275efc2ff38e52322ea264122d72729dca2b2", size = 96014, upload-time = "2026-01-11T11:22:26.138Z" }, + { url = "https://files.pythonhosted.org/packages/b8/4e/75dab8586e268424202d3a1997ef6014919c941b50642a1682df43204c22/tomli-2.4.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:5b5807f3999fb66776dbce568cc9a828544244a8eb84b84b9bafc080c99597b9", size = 163339, upload-time = "2026-01-11T11:22:27.143Z" }, + { url = "https://files.pythonhosted.org/packages/06/e3/b904d9ab1016829a776d97f163f183a48be6a4deb87304d1e0116a349519/tomli-2.4.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c084ad935abe686bd9c898e62a02a19abfc9760b5a79bc29644463eaf2840cb0", size = 159490, upload-time = "2026-01-11T11:22:28.399Z" }, + { url = "https://files.pythonhosted.org/packages/e3/5a/fc3622c8b1ad823e8ea98a35e3c632ee316d48f66f80f9708ceb4f2a0322/tomli-2.4.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f2e3955efea4d1cfbcb87bc321e00dc08d2bcb737fd1d5e398af111d86db5df", size = 269398, upload-time = "2026-01-11T11:22:29.345Z" }, + { url = "https://files.pythonhosted.org/packages/fd/33/62bd6152c8bdd4c305ad9faca48f51d3acb2df1f8791b1477d46ff86e7f8/tomli-2.4.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e0fe8a0b8312acf3a88077a0802565cb09ee34107813bba1c7cd591fa6cfc8d", size = 276515, upload-time = "2026-01-11T11:22:30.327Z" }, + { url = "https://files.pythonhosted.org/packages/4b/ff/ae53619499f5235ee4211e62a8d7982ba9e439a0fb4f2f351a93d67c1dd2/tomli-2.4.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:413540dce94673591859c4c6f794dfeaa845e98bf35d72ed59636f869ef9f86f", size = 273806, upload-time = "2026-01-11T11:22:32.56Z" }, + { url = "https://files.pythonhosted.org/packages/47/71/cbca7787fa68d4d0a9f7072821980b39fbb1b6faeb5f5cf02f4a5559fa28/tomli-2.4.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0dc56fef0e2c1c470aeac5b6ca8cc7b640bb93e92d9803ddaf9ea03e198f5b0b", size = 281340, upload-time = "2026-01-11T11:22:33.505Z" }, + { url = "https://files.pythonhosted.org/packages/f5/00/d595c120963ad42474cf6ee7771ad0d0e8a49d0f01e29576ee9195d9ecdf/tomli-2.4.0-cp314-cp314t-win32.whl", hash = "sha256:d878f2a6707cc9d53a1be1414bbb419e629c3d6e67f69230217bb663e76b5087", size = 108106, upload-time = "2026-01-11T11:22:34.451Z" }, + { url = "https://files.pythonhosted.org/packages/de/69/9aa0c6a505c2f80e519b43764f8b4ba93b5a0bbd2d9a9de6e2b24271b9a5/tomli-2.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2add28aacc7425117ff6364fe9e06a183bb0251b03f986df0e78e974047571fd", size = 120504, upload-time = "2026-01-11T11:22:35.764Z" }, + { url = "https://files.pythonhosted.org/packages/b3/9f/f1668c281c58cfae01482f7114a4b88d345e4c140386241a1a24dcc9e7bc/tomli-2.4.0-cp314-cp314t-win_arm64.whl", hash = "sha256:2b1e3b80e1d5e52e40e9b924ec43d81570f0e7d09d11081b797bc4692765a3d4", size = 99561, upload-time = "2026-01-11T11:22:36.624Z" }, + { url = "https://files.pythonhosted.org/packages/23/d1/136eb2cb77520a31e1f64cbae9d33ec6df0d78bdf4160398e86eec8a8754/tomli-2.4.0-py3-none-any.whl", hash = "sha256:1f776e7d669ebceb01dee46484485f43a4048746235e683bcdffacdf1fb4785a", size = 14477, upload-time = "2026-01-11T11:22:37.446Z" }, ] [[package]] name = "typing-extensions" -version = "4.14.0" +version = "4.15.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d1/bc/51647cd02527e87d05cb083ccc402f93e441606ff1f01739a62c8ad09ba5/typing_extensions-4.14.0.tar.gz", hash = "sha256:8676b788e32f02ab42d9e7c61324048ae4c6d844a399eebace3d4979d75ceef4", size = 107423, upload-time = "2025-06-02T14:52:11.399Z" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/69/e0/552843e0d356fbb5256d21449fa957fa4eff3bbc135a74a691ee70c7c5da/typing_extensions-4.14.0-py3-none-any.whl", hash = "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af", size = 43839, upload-time = "2025-06-02T14:52:10.026Z" }, + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, ] [[package]] name = "urllib3" -version = "2.5.0" +version = "2.6.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185, upload-time = "2025-06-18T14:07:41.644Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795, upload-time = "2025-06-18T14:07:40.39Z" }, + { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, ] From 90ddb40b01b56277d74063a56b38235ffbc89887 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20H=C3=B6lscher?= Date: Mon, 2 Feb 2026 13:11:16 +0100 Subject: [PATCH 19/22] Fix bug in coupled sim Wrong indexing of mesh loss (node indices and then time steps). --- plutho/coupled_sim.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plutho/coupled_sim.py b/plutho/coupled_sim.py index b8d7bd3..5e463d6 100644 --- a/plutho/coupled_sim.py +++ b/plutho/coupled_sim.py @@ -198,10 +198,10 @@ def simulate( # Calculate mech losses which are sources in heat cond sim avg_mech_loss = np.mean( self.thermo_piezo_sim.mech_loss[ - :, - -averaging_time_step_count: + -averaging_time_step_count:, + : ], - axis=1 + axis=0 ) self.thermo_sim.set_constant_volume_heat_source( avg_mech_loss, From 3f4a43f32f753e02c8f028b419e0bc5016dee659 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20H=C3=B6lscher?= Date: Mon, 2 Feb 2026 14:18:54 +0100 Subject: [PATCH 20/22] HB Move newton arc length to separate fun --- plutho/simulations/nonlinear/piezo_hb.py | 27 ++++-------------------- 1 file changed, 4 insertions(+), 23 deletions(-) diff --git a/plutho/simulations/nonlinear/piezo_hb.py b/plutho/simulations/nonlinear/piezo_hb.py index 813dd52..d369416 100644 --- a/plutho/simulations/nonlinear/piezo_hb.py +++ b/plutho/simulations/nonlinear/piezo_hb.py @@ -31,7 +31,6 @@ class NLPiezoHB(FEMSolver): # Harmonic balancing hb_order: int - def __init__( self, simulation_name: str, @@ -174,7 +173,7 @@ def newton_arclength( # Compute frequency load increment arc_length = 1.1 delta_freq_load_0 = arc_length/(np.linalg.norm(delta_u_p_0)) - ## Calculate direction + # Calculate direction current_stiffness_parameter = P@u_p/np.linalg.norm(u_p) if current_stiffness_parameter < 0: delta_freq_load_0 *= -1 @@ -228,7 +227,7 @@ def res_fun(u): x[:-1] = residual_fun(u[:-1], freq_last, freq_load_i) x[-1] = riks(u[:-1], freq_load_i) return x - + print("Testing jacobian") NLPiezoHB.test_jacobian_static( t, @@ -394,10 +393,10 @@ def dresidual_df(u, frequency, frequency_load): self.u = u self.frequencies = frequencies[:-1] - def simulate( self, frequencies: npt.NDArray, + amplitude: float, tolerance: float = 1e-6, max_iter: int = 100, newton_damping: float = 1 @@ -432,7 +431,6 @@ def simulate( u = np.zeros( (len(frequencies), 2*3*self.hb_order*number_of_nodes) ) - loads = np.zeros(len(frequencies)) # Linear tangent matrix can be created beforehand and scaled with # frequency later @@ -464,8 +462,6 @@ def residual_fun(u): f, frequency ) - def rhs_arc_len_fun(u): - return residual_fun(u) + f def tangent_fun(u): return tangent_linear + self.tangent_nonlinear( @@ -483,7 +479,6 @@ def tangent_fun(u): ) # Solve newton normal - """ u[frequency_index, :] = NLPiezoHB.newton( u_init, residual_fun, @@ -492,20 +487,6 @@ def tangent_fun(u): tolerance, newton_damping ) - """ - # Solve newton arclen - u_res, load_res = NLPiezoHB.newton_arclength( - u_init, - f, - rhs_arc_len_fun, - tangent_fun, - max_iter, - tolerance, - u[frequency_index-1, :], - loads[frequency_index-1] - ) - u[frequency_index, :] = u_res - loads[frequency_index] = load_res self.u = u @@ -570,7 +551,7 @@ def linear_system_matrix( k_blocks[j][j] = k_fem # ω part (c) - c_blocks[i][j] = np1 * c_fem + c_blocks[i][j] = np1 * c_fem c_blocks[j][i] = -np1 * c_fem # ω² part (m) From d8335ee8a0b2bddc3ae0af0bed1fb53344da817e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20H=C3=B6lscher?= Date: Wed, 4 Feb 2026 11:05:54 +0100 Subject: [PATCH 21/22] Fix HB for linear case BC applied on HB matrices. Flip sign of HB sin part in u. --- plutho/simulations/nonlinear/piezo_hb.py | 610 +++++++++++---------- plutho/simulations/nonlinear/piezo_time.py | 5 +- plutho/simulations/solver.py | 2 +- 3 files changed, 317 insertions(+), 300 deletions(-) diff --git a/plutho/simulations/nonlinear/piezo_hb.py b/plutho/simulations/nonlinear/piezo_hb.py index d369416..a8e8e5b 100644 --- a/plutho/simulations/nonlinear/piezo_hb.py +++ b/plutho/simulations/nonlinear/piezo_hb.py @@ -64,8 +64,7 @@ def assemble(self): def simulate_linear( self, frequency: float, - dirichlet_nodes: npt.NDArray, - amplitude: float + amplitude: npt.NDArray ) -> npt.NDArray: """Runs a linear simulation for one frequency and returns the whole u vector. @@ -80,13 +79,7 @@ def simulate_linear( c = self.c.copy() k = self.k.copy() - # Apply dirichlet bc to matrices - m, c, k = self._apply_dirichlet_bc( - m, - c, - k, - dirichlet_nodes - ) + dirichlet_nodes = np.array(self.dirichlet_nodes) # Prepare arrays f = self._get_load_vector( @@ -96,8 +89,15 @@ def simulate_linear( # Calculate system matrix angular_frequency = 2*np.pi*frequency - m, c, k = self.linear_system_matrix(m, c, k) - s = m*angular_frequency**2+c*angular_frequency+k + hb_m, hb_c, hb_k = self.linear_system_matrix(m, c, k) + hb_m, hb_c, hb_k = self._apply_dirichlet_bc( + hb_m, + hb_c, + hb_k, + dirichlet_nodes + ) + + s = hb_m*angular_frequency**2+hb_c*angular_frequency+hb_k # Solve system lu = slin.splu(s) @@ -113,7 +113,7 @@ def newton( newton_damping: float ): # Set initial value - u_i = u_init + u_i = u_init.copy() # Check if initial value is already sufficient residual = residual_fun(u_i) @@ -131,7 +131,7 @@ def newton( # TODO Faster than slin.spsolve? -> Change in other solvers? lu = slin.splu(tangent_matrix) delta_u = lu.solve(residual) - u_i_next = u_i - delta_u * newton_damping + u_i = u_i - delta_u * newton_damping # Update residual residual = residual_fun(u_i) @@ -140,263 +140,18 @@ def newton( norm = np.linalg.norm(residual) if norm < tolerance: # Newton converged - print(f"Newton converged after {i} iterations") - return u_i_next - - # Update for next iteration - u_i = u_i_next + print(f"Newton converged after {i+1} iterations") + return u_i if not converged: print(f"Newton did not converge: Maximum iteration ({norm})") return u_i - @staticmethod - def newton_arclength( - u_last: npt.NDArray, - freq_last: float, - freq_load_last: float, - tangent_u_fun: Callable, - tangent_vec_freq_load_fun: Callable, - residual_fun: Callable, - tolerance: float, - max_iter: int - ): - # Set initial value - u_0 = u_last - - # Predictor step - lu = slin.splu(tangent_u_fun(u_0, freq_last, freq_load_last)) - P = -tangent_vec_freq_load_fun(u_last, freq_last, freq_load_last) - delta_u_p_0 = lu.solve(P) - u_p = u_last + delta_u_p_0 - - # Compute frequency load increment - arc_length = 1.1 - delta_freq_load_0 = arc_length/(np.linalg.norm(delta_u_p_0)) - # Calculate direction - current_stiffness_parameter = P@u_p/np.linalg.norm(u_p) - if current_stiffness_parameter < 0: - delta_freq_load_0 *= -1 - freq_load_0 = freq_load_last + delta_freq_load_0 - - # Check if initial value is already sufficient - residual = residual_fun(u_0, freq_last, freq_load_0) - norm = np.linalg.norm(residual) - if norm < tolerance: - print("Initial value already sufficient") - return u_0, freq_load_0 - - # Set initial values for iteraion - u_i = u_0 - freq_load_i = freq_load_0 - - for i in range(max_iter): - # RIKS arc-length constraint - def riks(u, freq_load): - return ( - (u_0-u_last).T@(u-u_0) - + (freq_load_0-freq_load_last)*(freq_load-freq_load_0) - ) - - # RIKS arc-length constrained derived after load - def riks_dload(): - return freq_load_0-freq_load_last - - # RIKS arc-length constrained derived after u - def riks_du(): - return u_0-u_last - - # tmp: Test Tangent matrix - k_tan = tangent_u_fun(u_i, freq_last, freq_load_i) - P = tangent_vec_freq_load_fun(u_i, freq_last, freq_load_i) - P_mat = P.reshape(-1, 1) - du = riks_du() - du_mat = sparse.csc_matrix(du.reshape(1, -1)) - dl_mat = sparse.csc_matrix([[riks_dload()]]) - blocks = [ - [k_tan, -P_mat], - [du_mat, dl_mat] - ] - t = sparse.block_array(blocks, format="csc") - x = np.zeros(u_0.shape[0]+1) - x[:-1] = u_i - x[-1] = freq_load_i - - def res_fun(u): - x = np.zeros(u.shape[0]) - x[:-1] = residual_fun(u[:-1], freq_last, freq_load_i) - x[-1] = riks(u[:-1], freq_load_i) - return x - - print("Testing jacobian") - NLPiezoHB.test_jacobian_static( - t, - x, - res_fun - ) - - # Solve for increments - P = -tangent_vec_freq_load_fun(u_i, freq_last, freq_load_i) - u_p_next = lu.solve(P) - u_g_next = lu.solve(-residual_fun(u_i, freq_last, freq_load_i)) - - # Calculate increments - denominator = riks_dload()+riks_du()@u_p_next - print(denominator) - if denominator < 1e-14: - print("Singular system") - break - delta_freq_load_i = - ( - riks(u_i, freq_load_i) - + riks_du()@u_g_next - )/denominator - delta_u_i = delta_freq_load_i*u_p_next+u_g_next - - # Increment - freq_load_i_next = freq_load_i + delta_freq_load_i - u_i_next = u_i + delta_u_i - - # Update residual - residual = residual_fun(u_i_next, freq_last, freq_load_i_next) - - # Check for convergence - norm = np.linalg.norm(residual) - if norm < tolerance: - # Newton converged - print(f"Newton converged after {i+1} iterations") - return u_i_next, freq_load_i_next - elif np.abs(norm) > 1e10: - print("Newton diverged") - break - - # Update for next iteration - u_i = u_i_next - freq_load_i = freq_load_i_next - lu = slin.splu(tangent_u_fun(u_i, freq_last, freq_load_i)) - - print(f"Newton did not converged: Maximum iteration (norm: {norm})") - return u_i, freq_load_i - - def simulate_path( - self, - frequency_start: float, - frequency_steps: int, - frequency_default_step: float, - amplitude: float, - tolerance: float = 1e-6, - max_iter: int = 100, - ): - dirichlet_nodes = np.array(self.dirichlet_nodes) - number_of_nodes = len(self.mesh_data.nodes) - - m = self.m.copy() - c = self.c.copy() - k = self.k.copy() - - # Apply dirichlet bc to matrices - m, c, k = self._apply_dirichlet_bc( - m, - c, - k, - dirichlet_nodes - ) - self.nonlinearity.apply_dirichlet_bc(dirichlet_nodes) - - # Prepare array and guess initial value using linear simulation - u = np.zeros( - (frequency_steps, 2*3*self.hb_order*number_of_nodes) - ) - u[0, :] = self.simulate_linear( - frequency_start, - dirichlet_nodes, - amplitude - ) - frequency_loads = np.zeros(frequency_steps) - frequency_loads[0] = 1 - frequencies = np.zeros(frequency_steps+1) - frequencies[0] = frequency_start - - # Linear tangent matrix can be created beforehand and scaled with - # frequency later - # Since its linear it is equal to the tangent matrix for the linear - # part - m_tan, c_tan, k_tan = self.linear_system_matrix(m, c, k) - - print("Starting harmonic balancing simulation") - for index in range(frequency_steps): - frequency = frequencies[index] - print(f"Frequency index: {index}, Frequency: {frequency}") - - # Get load vector - load_vector = self._get_load_vector( - dirichlet_nodes, - amplitude - ) - - # Define residual and tangent functions for newton - def residual_fun(u, frequency, frequency_load): - angular_frequency = 2*np.pi*frequency*frequency_load - tangent_linear = ( - k_tan - + angular_frequency * c_tan - + angular_frequency**2 * m_tan - ) - - return self.residual( - tangent_linear, - u, - load_vector, - frequency - ) - - def tangent_fun(u, frequency, frequency_load): - angular_frequency = 2*np.pi*frequency*frequency_load - return ( - k_tan - + angular_frequency*c_tan - + angular_frequency**2*m_tan - + self.tangent_nonlinear(u, frequency) - ).tocsc() - - def dresidual_df(u, frequency, frequency_load): - # Residual derived after frequency load factor - angular_frequency = 2*np.pi*frequency - return ( - 2*frequency_load*angular_frequency**2*m_tan - + angular_frequency*c_tan - )@u - - if index > 0: - u_last = u[index-1, :] - else: - u_last = self.simulate_linear( - frequency, - dirichlet_nodes, - amplitude - ) - - # Solve newton arclen - u_res, frequency_load = NLPiezoHB.newton_arclength( - u_last, - frequencies[index], - frequency_loads[index], - tangent_fun, - dresidual_df, - residual_fun, - tolerance, - max_iter - ) - u[index, :] = u_res - frequency_loads[index] = frequency_load - frequencies[index+1] = frequency_load*frequency - - self.u = u - self.frequencies = frequencies[:-1] + return None def simulate( self, frequencies: npt.NDArray, - amplitude: float, tolerance: float = 1e-6, max_iter: int = 100, newton_damping: float = 1 @@ -413,30 +168,31 @@ def simulate( dirichlet_nodes = np.array(self.dirichlet_nodes) dirichlet_values = np.array(self.dirichlet_values) number_of_nodes = len(self.mesh_data.nodes) + dof = 3*number_of_nodes m = self.m.copy() c = self.c.copy() k = self.k.copy() - # Apply dirichlet bc to matrices - m, c, k = self._apply_dirichlet_bc( - m, - c, - k, - dirichlet_nodes - ) - self.nonlinearity.apply_dirichlet_bc(dirichlet_nodes) - # Prepare array and guess initial value using linear simulation u = np.zeros( - (len(frequencies), 2*3*self.hb_order*number_of_nodes) + (len(frequencies), 2*self.hb_order*dof) ) # Linear tangent matrix can be created beforehand and scaled with # frequency later # Since its linear it is equal to the tangent matrix for the linear # part - m_tan, c_tan, k_tan = self.linear_system_matrix(m, c, k) + m_hb, c_hb, k_hb = self.linear_system_matrix(m, c, k) + + # Apply dirichlet bc to matrices + self.nonlinearity.apply_dirichlet_bc(dirichlet_nodes) + m_hb, c__hb, k_hb = self._apply_dirichlet_bc( + m_hb, + c_hb, + k_hb, + dirichlet_nodes + ) print("Starting harmonic balancing simulation") for frequency_index, frequency in enumerate(frequencies): @@ -444,8 +200,8 @@ def simulate( # Construct whole linear tangent matrix using the current frequency angular_frequency = 2*np.pi*frequency tangent_linear = ( - k_tan + angular_frequency * c_tan + - angular_frequency**2 * m_tan + k_hb + angular_frequency * c_hb + + angular_frequency**2 * m_hb ).tocsc() # Get load vector @@ -474,8 +230,7 @@ def tangent_fun(u): else: u_init = self.simulate_linear( frequencies[0], - dirichlet_nodes, - amplitude + dirichlet_values[:, 0] ) # Solve newton normal @@ -488,6 +243,10 @@ def tangent_fun(u): newton_damping ) + # Flip sign of all sin parts + for i in range(self.hb_order): + u[:, (2*i+1)*dof:(2*i+2)*dof] *= -1 + self.u = u def residual( @@ -507,9 +266,9 @@ def residual( def linear_system_matrix( self, - m_fem: sparse.csc_array, - c_fem: sparse.csc_array, - k_fem: sparse.csc_array + m_fem: sparse.lil_array, + c_fem: sparse.lil_array, + k_fem: sparse.lil_array ) -> Tuple[sparse.csc_array, sparse.csc_array, sparse.csc_array]: """Calculates the matrices for the linear system. Since they have to incoroporate the simulation frequency, a tuple of 3 matrices is @@ -559,9 +318,9 @@ def linear_system_matrix( m_blocks[j][j] = -(np1**2) * m_fem # Convert to sparse once - k_hb = sparse.block_array(k_blocks, format="csc") - c_hb = sparse.block_array(c_blocks, format="csc") - m_hb = sparse.block_array(m_blocks, format="csc") + k_hb = sparse.block_array(k_blocks, format="lil") + c_hb = sparse.block_array(c_blocks, format="lil") + m_hb = sparse.block_array(m_blocks, format="lil") return m_hb, c_hb, k_hb @@ -764,30 +523,40 @@ def _get_load_vector( # charge density is 0. f = np.zeros(2*self.hb_order*3*number_of_nodes, dtype=np.float64) - # TODO: Right now only the base frequency cosine part is set - # Set dirichlet bc + # NOTE: Only sets the cosine part f[nodes] = amplitude return f def _apply_dirichlet_bc( self, - m: sparse.lil_array, - c: sparse.lil_array, - k: sparse.lil_array, - nodes: npt.NDArray + m_hb: sparse.lil_array, + c_hb: sparse.lil_array, + k_hb: sparse.lil_array, + nodes: npt.NDArray, ) -> Tuple[ sparse.csc_array, sparse.csc_array, sparse.csc_array ]: - # Set rows of matrices to 0 and diagonal of K to 1 (at node points) - m[nodes, :] = 0 - c[nodes, :] = 0 - k[nodes, :] = 0 - k[nodes, nodes] = 1 + # NOTE: For every component (cos or sin) for every harmonic set the + # corresponding rows + dof = 3*len(self.mesh_data.nodes) + + for i in range(self.hb_order): + # cos + m_hb[nodes+i*2*dof, :] = 0 + c_hb[nodes+i*2*dof, :] = 0 + k_hb[nodes+i*2*dof, :] = 0 + k_hb[nodes+i*2*dof, nodes+i*2*dof] = 1 + + # sin + m_hb[nodes+i*2*dof+dof, :] = 0 + c_hb[nodes+i*2*dof+dof, :] = 0 + k_hb[nodes+i*2*dof+dof, :] = 0 + k_hb[nodes+i*2*dof+dof, nodes+i*2*dof+dof] = 1 - return m.tocsc(), c.tocsc(), k.tocsc() + return m_hb.tocsc(), c_hb.tocsc(), k_hb.tocsc() @staticmethod def test_jacobian_static( @@ -895,15 +664,264 @@ def calculate_charge(self, electrode_name: str, is_complex: bool = True): electrode_name: Name of the electrode phyiscal group. is_complex: """ - nodes, _ = self.mesh.get_mesh_nodes_and_elements() - dof = 3*len(nodes) + dof = 3*len(self.mesh_data.nodes) hb_u = np.copy(self.u) q = np.zeros(shape=(self.hb_order, hb_u.shape[0]), dtype=np.complex128) for n in range(self.hb_order): + # TODO: The imaginary part is flipped (must be a -1j here?) self.u = hb_u[:, 2*n*dof:(2*n+1)*dof] \ + 1j*hb_u[:, (2*n+1)*dof:(2*n+2)*dof] super().calculate_charge(electrode_name, is_complex=True) q[n, :] = self.q + # Reset variables to initial values since they are used in + # super().calculate_charge(..) self.u = hb_u self.q = q + + # ========= + # TODO: WIP + # ========= + + @staticmethod + def newton_arclength( + u_last: npt.NDArray, + freq_last: float, + freq_load_last: float, + tangent_u_fun: Callable, + tangent_vec_freq_load_fun: Callable, + residual_fun: Callable, + tolerance: float, + max_iter: int + ): + # Set initial value + u_0 = u_last + + # Predictor step + lu = slin.splu(tangent_u_fun(u_0, freq_last, freq_load_last)) + P = -tangent_vec_freq_load_fun(u_last, freq_last, freq_load_last) + delta_u_p_0 = lu.solve(P) + u_p = u_last + delta_u_p_0 + + # Compute frequency load increment + arc_length = 1.1 + delta_freq_load_0 = arc_length/(np.linalg.norm(delta_u_p_0)) + # Calculate direction + current_stiffness_parameter = P@u_p/np.linalg.norm(u_p) + if current_stiffness_parameter < 0: + delta_freq_load_0 *= -1 + freq_load_0 = freq_load_last + delta_freq_load_0 + + # Check if initial value is already sufficient + residual = residual_fun(u_0, freq_last, freq_load_0) + norm = np.linalg.norm(residual) + if norm < tolerance: + print("Initial value already sufficient") + return u_0, freq_load_0 + + # Set initial values for iteraion + u_i = u_0 + freq_load_i = freq_load_0 + + for i in range(max_iter): + # RIKS arc-length constraint + def riks(u, freq_load): + return ( + (u_0-u_last).T@(u-u_0) + + (freq_load_0-freq_load_last)*(freq_load-freq_load_0) + ) + + # RIKS arc-length constrained derived after load + def riks_dload(): + return freq_load_0-freq_load_last + + # RIKS arc-length constrained derived after u + def riks_du(): + return u_0-u_last + + # tmp: Test Tangent matrix + k_tan = tangent_u_fun(u_i, freq_last, freq_load_i) + P = tangent_vec_freq_load_fun(u_i, freq_last, freq_load_i) + P_mat = P.reshape(-1, 1) + du = riks_du() + du_mat = sparse.csc_matrix(du.reshape(1, -1)) + dl_mat = sparse.csc_matrix([[riks_dload()]]) + blocks = [ + [k_tan, -P_mat], + [du_mat, dl_mat] + ] + t = sparse.block_array(blocks, format="csc") + x = np.zeros(u_0.shape[0]+1) + x[:-1] = u_i + x[-1] = freq_load_i + + def res_fun(u): + x = np.zeros(u.shape[0]) + x[:-1] = residual_fun(u[:-1], freq_last, freq_load_i) + x[-1] = riks(u[:-1], freq_load_i) + return x + + print("Testing jacobian") + NLPiezoHB.test_jacobian_static( + t, + x, + res_fun + ) + + # Solve for increments + P = -tangent_vec_freq_load_fun(u_i, freq_last, freq_load_i) + u_p_next = lu.solve(P) + u_g_next = lu.solve(-residual_fun(u_i, freq_last, freq_load_i)) + + # Calculate increments + denominator = riks_dload()+riks_du()@u_p_next + print(denominator) + if denominator < 1e-14: + print("Singular system") + break + delta_freq_load_i = - ( + riks(u_i, freq_load_i) + + riks_du()@u_g_next + )/denominator + delta_u_i = delta_freq_load_i*u_p_next+u_g_next + + # Increment + freq_load_i_next = freq_load_i + delta_freq_load_i + u_i_next = u_i + delta_u_i + + # Update residual + residual = residual_fun(u_i_next, freq_last, freq_load_i_next) + + # Check for convergence + norm = np.linalg.norm(residual) + if norm < tolerance: + # Newton converged + print(f"Newton converged after {i+1} iterations") + return u_i_next, freq_load_i_next + elif np.abs(norm) > 1e10: + print("Newton diverged") + break + + # Update for next iteration + u_i = u_i_next + freq_load_i = freq_load_i_next + lu = slin.splu(tangent_u_fun(u_i, freq_last, freq_load_i)) + + print(f"Newton did not converged: Maximum iteration (norm: {norm})") + return u_i, freq_load_i + + def simulate_path( + self, + frequency_start: float, + frequency_steps: int, + frequency_default_step: float, + amplitude: float, + tolerance: float = 1e-6, + max_iter: int = 100, + ): + dirichlet_nodes = np.array(self.dirichlet_nodes) + number_of_nodes = len(self.mesh_data.nodes) + + m = self.m.copy() + c = self.c.copy() + k = self.k.copy() + + # Apply dirichlet bc to matrices + m, c, k = self._apply_dirichlet_bc( + m, + c, + k, + dirichlet_nodes + ) + self.nonlinearity.apply_dirichlet_bc(dirichlet_nodes) + + # Prepare array and guess initial value using linear simulation + u = np.zeros( + (frequency_steps, 2*3*self.hb_order*number_of_nodes) + ) + u[0, :] = self.simulate_linear( + frequency_start, + dirichlet_nodes, + amplitude + ) + frequency_loads = np.zeros(frequency_steps) + frequency_loads[0] = 1 + frequencies = np.zeros(frequency_steps+1) + frequencies[0] = frequency_start + + # Linear tangent matrix can be created beforehand and scaled with + # frequency later + # Since its linear it is equal to the tangent matrix for the linear + # part + m_tan, c_tan, k_tan = self.linear_system_matrix(m, c, k) + + print("Starting harmonic balancing simulation") + for index in range(frequency_steps): + frequency = frequencies[index] + print(f"Frequency index: {index}, Frequency: {frequency}") + + # Get load vector + load_vector = self._get_load_vector( + dirichlet_nodes, + amplitude + ) + + # Define residual and tangent functions for newton + def residual_fun(u, frequency, frequency_load): + angular_frequency = 2*np.pi*frequency*frequency_load + tangent_linear = ( + k_tan + + angular_frequency * c_tan + + angular_frequency**2 * m_tan + ) + + return self.residual( + tangent_linear, + u, + load_vector, + frequency + ) + + def tangent_fun(u, frequency, frequency_load): + angular_frequency = 2*np.pi*frequency*frequency_load + return ( + k_tan + + angular_frequency*c_tan + + angular_frequency**2*m_tan + + self.tangent_nonlinear(u, frequency) + ).tocsc() + + def dresidual_df(u, frequency, frequency_load): + # Residual derived after frequency load factor + angular_frequency = 2*np.pi*frequency + return ( + 2*frequency_load*angular_frequency**2*m_tan + + angular_frequency*c_tan + ) + + if index > 0: + u_last = u[index-1, :] + else: + u_last = self.simulate_linear( + frequency, + dirichlet_nodes, + amplitude + ) + + # Solve newton arclen + u_res, frequency_load = NLPiezoHB.newton_arclength( + u_last, + frequencies[index], + frequency_loads[index], + tangent_fun, + dresidual_df, + residual_fun, + tolerance, + max_iter + ) + u[index, :] = u_res + frequency_loads[index] = frequency_load + frequencies[index+1] = frequency_load*frequency + + self.u = u + self.frequencies = frequencies[:-1] diff --git a/plutho/simulations/nonlinear/piezo_time.py b/plutho/simulations/nonlinear/piezo_time.py index 852b87a..72a02c8 100644 --- a/plutho/simulations/nonlinear/piezo_time.py +++ b/plutho/simulations/nonlinear/piezo_time.py @@ -29,7 +29,6 @@ class NLPiezoTime(FEMSolver): # Nonlinear simulation nonlinearity: Nonlinearity - def __init__( self, simulation_name: str, @@ -203,8 +202,8 @@ def residual(next_u, current_u, v, a, f): u_i = u_i_next if not self.converged: print( - f"Newton did not converge at time index: {time_index}. " - f"Choosing best value: {best_norm}" + f"Newton did not converge at time index:" + f" {time_index}. Choosing best value: {best_norm}" ) next_u = best_u_i else: diff --git a/plutho/simulations/solver.py b/plutho/simulations/solver.py index d16a567..9713e65 100644 --- a/plutho/simulations/solver.py +++ b/plutho/simulations/solver.py @@ -311,7 +311,7 @@ def load_simulation_settings(cls, simulation_folder: str): physical_group_name = bc["physical_group_name"] values = np.array(list(bc["values"])) - sim.add_dirichlet_bc(field_type, physical_group_name,values) + sim.add_dirichlet_bc(field_type, physical_group_name, values) return sim From ddd0da14b968b2a9c2d8fb641ff851bae677f591 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20H=C3=B6lscher?= Date: Wed, 4 Feb 2026 11:11:05 +0100 Subject: [PATCH 22/22] Remove TODO comment --- plutho/simulations/nonlinear/piezo_hb.py | 1 - 1 file changed, 1 deletion(-) diff --git a/plutho/simulations/nonlinear/piezo_hb.py b/plutho/simulations/nonlinear/piezo_hb.py index a8e8e5b..ada57be 100644 --- a/plutho/simulations/nonlinear/piezo_hb.py +++ b/plutho/simulations/nonlinear/piezo_hb.py @@ -668,7 +668,6 @@ def calculate_charge(self, electrode_name: str, is_complex: bool = True): hb_u = np.copy(self.u) q = np.zeros(shape=(self.hb_order, hb_u.shape[0]), dtype=np.complex128) for n in range(self.hb_order): - # TODO: The imaginary part is flipped (must be a -1j here?) self.u = hb_u[:, 2*n*dof:(2*n+1)*dof] \ + 1j*hb_u[:, (2*n+1)*dof:(2*n+2)*dof] super().calculate_charge(electrode_name, is_complex=True)