From 43ebb29d212e578a3960f54340e7f4275c68db8d Mon Sep 17 00:00:00 2001 From: JanHab <Jan.Habscheid@web.de> Date: Mon, 19 Aug 2024 19:08:50 +0200 Subject: [PATCH] Eq02 now really eq02, before by accident Eq04 --- ...ncompressibleValidationAnalyticalMethod.py | 21 ++- .../ValidationSimplifiedSystem.py | 84 +++++++++++ src/Eq02.py | 141 ++++++------------ src/Helper_DoubleLayerCapacity.py | 11 +- src/__pycache__/Eq02.cpython-312.pyc | Bin 20555 -> 16214 bytes ...Helper_DoubleLayerCapacity.cpython-312.pyc | Bin 10503 -> 10327 bytes 6 files changed, 150 insertions(+), 107 deletions(-) create mode 100644 examples/ReproducableCode/ValidationSimplifiedSystem.py diff --git a/examples/ReproducableCode/DoubleLayerCapacity/IncompressibleValidationAnalyticalMethod.py b/examples/ReproducableCode/DoubleLayerCapacity/IncompressibleValidationAnalyticalMethod.py index f2cfffa..c17780a 100644 --- a/examples/ReproducableCode/DoubleLayerCapacity/IncompressibleValidationAnalyticalMethod.py +++ b/examples/ReproducableCode/DoubleLayerCapacity/IncompressibleValidationAnalyticalMethod.py @@ -36,7 +36,7 @@ NA = 6.022e+23 # [1/mol] - Avogadro constant nR_mol = 55 nR_m = nR_mol * NA * 1/(1e-3)# [1/m^3] pR = 1.01325 * 1e+5 # [Pa] -LR = 20e-8 +LR = 20e-9 chi = 80 # [-] # Parameter and bcs for the electrolyte @@ -54,12 +54,13 @@ p_right = 0 number_cells = 1024 relax_param = 0.03 p_right = 0 +rtol = 1e-4 # ! Change back to 1e-8 # phi^L domain -Vol_start = 0 +Vol_start = 0.1 # ! Change back to 0 Volt_end = 0.75 -n_Volts = 5#0 +n_Volts = 25#0 phi_left = np.linspace(Vol_start, Volt_end, n_Volts) * e0/(k*T) @@ -68,7 +69,7 @@ phi_left = np.linspace(Vol_start, Volt_end, n_Volts) * e0/(k*T) # Solution vectors y_A_num, y_C_num, y_S_num, phi_num, p_num, x_num = [], [], [], [], [], [] for i, phi_bcs in enumerate(phi_left): - y_A_, y_C_, phi_, p_, x_ = solve_System_2eq(phi_bcs, phi_right, p_right, z_A, z_C, y_R, y_R, K, Lambda2, a2, number_cells, solvation=kappa, refinement_style='hard_log', rtol=1e-10, max_iter=2500, return_type='Vector', relax_param=relax_param) + y_A_, y_C_, phi_, p_, x_ = solve_System_2eq(phi_bcs, phi_right, p_right, z_A, z_C, y_R, y_R, K, Lambda2, a2, number_cells, solvation=kappa, refinement_style='hard_log', rtol=rtol, max_iter=2500, return_type='Vector', relax_param=relax_param) y_S_ = 1 - y_A_ - y_C_ y_A_num.append(y_A_) y_C_num.append(y_C_) @@ -82,9 +83,9 @@ for j in range(len(phi_left)): Q_num.append(Q_num_(y_A_num[j], y_C_num[j], n(p_num[j], K), x_num[j])) Q_num = np.array(Q_num) -# dx_ = phi_left[1] - phi_left[0] # [1/V], Assumption: phi^L is uniformly distributed -# C_DL_num = (Q_num[1:] - Q_num[:-1])/dx_ # [µAs/cm³] -# C_DL_num = np.array(C_DL_num) +dx_ = phi_left[1] - phi_left[0] # [1/V], Assumption: phi^L is uniformly distributed +C_DL_num = (Q_num[1:] - Q_num[:-1])/dx_ # [µAs/cm³] +C_DL_num = np.array(C_DL_num) C_dl_num = C_dl(Q_num, phi_left) @@ -115,4 +116,8 @@ plt.legend() plt.xlabel('$\delta \\varphi$ [-]') plt.ylabel('$C_{dl,num} - C_{dl,ana} [-]$') plt.tight_layout() -plt.show() \ No newline at end of file +plt.show() + +print('phi_left:', phi_left) +print('Q_num:', Q_num) +print('Q_ana:', Q_ana) \ No newline at end of file diff --git a/examples/ReproducableCode/ValidationSimplifiedSystem.py b/examples/ReproducableCode/ValidationSimplifiedSystem.py new file mode 100644 index 0000000..d8a2d83 --- /dev/null +++ b/examples/ReproducableCode/ValidationSimplifiedSystem.py @@ -0,0 +1,84 @@ +''' +Jan Habscheid +Jan.Habscheid@rwth-aachen.de + +This script is used to validate the simplification of the system of four equations to a system of two equations in the one-dimensional equilibrium case. +''' + +# import the src file needed to solve the system of equations +import sys +import os + +# Add the src directory to the sys.path +src_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), '../..', 'src') +sys.path.insert(0, src_path) + +from Eq04 import solve_System_4eq +from Eq02 import solve_System_2eq + +# Remove the src directory from sys.path after import +del sys.path[0] + +# Further imports +import matplotlib.pyplot as plt +import numpy as np +import pandas as pd + +# Define the parameters and boundary conditions +phi_left = 4.0 +phi_right = 0.0 +p_right = 0.0 +y_A_R = 0.01 +y_C_R = 0.01 +z_A = -1.0 +z_C = 1.0 +K = 'incompressible' +Lambda2 = 8.553e-6 +a2 = 7.5412e-4 +solvation = 15 +number_cells = 1024#*4 +refinement_style = 'log' +rtol = 1e-8 + +# solve the complete system +y_A_4eq, y_C_4eq, phi_4eq, p_4eq, x_4eq = solve_System_4eq(phi_left, phi_right, p_right, z_A, z_C, y_A_R, y_C_R, K, Lambda2, a2, number_cells, solvation=solvation, relax_param=0.05, refinement_style='hard_log', return_type='Vector', max_iter=1_000, rtol=rtol) + +# solve the simplified system +y_A_2eq, y_C_2eq, phi_2eq, p_2eq, x_2eq = solve_System_2eq(phi_left, phi_right, p_right, z_A, z_C, y_A_R, y_C_R, K, Lambda2, a2, number_cells, solvation=solvation, relax_param=0.05, refinement_style='hard_log', return_type='Vector', max_iter=1_000, rtol=rtol) + +# Evaluate the difference +plt.figure() +# plt.plot(x_4eq, y_A_4eq, label='y_A_4eq') +# plt.plot(x_2eq, y_A_2eq, label='y_A_2eq') +plt.plot(x_4eq, y_A_4eq - y_A_2eq, label='y_A_4eq - y_A_2eq') +plt.grid() +plt.xlim(0, 0.05) +plt.legend() +plt.show() + +plt.figure() +# plt.plot(x_4eq, y_C_4eq, label='y_C_4eq') +# plt.plot(x_2eq, y_C_2eq, label='y_C_2eq') +plt.plot(x_4eq, y_C_4eq - y_C_2eq, label='y_C_4eq - y_C_2eq') +plt.grid() +plt.xlim(0, 0.05) +plt.legend() +plt.show() + +plt.figure() +# plt.plot(x_4eq, phi_4eq, label='phi_4eq') +# plt.plot(x_2eq, phi_2eq, label='phi_2eq') +plt.plot(x_4eq, phi_4eq - phi_2eq, label='phi_4eq - phi_2eq') +plt.grid() +plt.xlim(0, 0.05) +plt.legend() +plt.show() + +plt.figure() +# plt.plot(x_4eq, p_4eq, label='p_4eq') +# plt.plot(x_2eq, p_2eq, label='p_2eq') +plt.plot(x_4eq, p_4eq - p_2eq, label='p_4eq - p_2eq') +plt.grid() +plt.xlim(0, 0.05) +plt.legend() +plt.show() \ No newline at end of file diff --git a/src/Eq02.py b/src/Eq02.py index 45901a1..7ecd7d0 100644 --- a/src/Eq02.py +++ b/src/Eq02.py @@ -8,7 +8,7 @@ from mpi4py import MPI from dolfinx import mesh, fem, log from dolfinx.fem.petsc import NonlinearProblem from dolfinx.nls.petsc import NewtonSolver -from ufl import TestFunctions, split, dot, grad, dx, inner, ln, Mesh +from ufl import TestFunctions, split, dot, grad, dx, inner, Mesh, exp from basix.ufl import element, mixed_element import matplotlib.pyplot as plt @@ -51,7 +51,7 @@ def create_refined_mesh(refinement_style:str, number_cells:int) -> Mesh: msh = mesh.create_mesh(MPI.COMM_WORLD, cells_np, coordinates_np_, domain) return msh -def solve_System_2eq(phi_left:float, phi_right:float, p_right:float, z_A:float, z_C:float, y_A_R:float, y_C_R:float, K:float|str, Lambda2:float, a2:float, number_cells:int, solvation:float = 0, PoissonBoltzmann:bool=False, relax_param:float=None, x0:float=0, x1:float=1, refinement_style:str='uniform', return_type:str='Scalar', rtol:float=1e-8, max_iter:float=500): +def solve_System_2eq(phi_left:float, phi_right:float, p_right:float, z_A:float, z_C:float, y_A_R:float, y_C_R:float, K:float|str, Lambda2:float, a2:float, number_cells:int, solvation:float = 0, relax_param:float=None, x0:float=0, x1:float=1, refinement_style:str='uniform', return_type:str='Scalar', rtol:float=1e-8, max_iter:float=500): ''' Solve the simplified dimensionless system of equations presented in: Numerical Treatment of a Thermodynamically Consistent Electrolyte Model, B.Sc. Thesis Habscheid 2024 @@ -96,8 +96,6 @@ def solve_System_2eq(phi_left:float, phi_right:float, p_right:float, z_A:float, Number of cells in the mesh solvation : float, optional solvation number, by default 0 - PoissonBoltzmann : bool, optional - Solve classical Nernst-Planck model with the use of the Poisson-Boltzmann formulation if True, else solve the presented model by Dreyer, Guhlke, Müller, by default False relax_param : float, optional Relaxation parameter for the Newton solver xₙ₊₁ = γ xₙ f(xₙ)/f'(xₙ) with γ the relaxation parameter @@ -122,6 +120,8 @@ def solve_System_2eq(phi_left:float, phi_right:float, p_right:float, z_A:float, Returns atomic fractions for species A and C, electric potential, pressure, and the mesh If return_type is 'Vector', the solution is returned as numpy arrays ''' + if return_type == 'Scalar': + raise NotImplementedError('Scalar return type is not implemented yet') # Define boundaries of the domain x0 = 0 x1 = 1 @@ -143,20 +143,18 @@ def solve_System_2eq(phi_left:float, phi_right:float, p_right:float, z_A:float, CG1_elem = element('Lagrange', msh.basix_cell(), 1) # Define Mixed Function Space - W_elem = mixed_element([CG1_elem, CG1_elem, CG1_elem, CG1_elem]) + W_elem = mixed_element([CG1_elem, CG1_elem])#, CG1_elem, CG1_elem]) W = fem.functionspace(msh, W_elem) # Define Trial- and Testfunctions u = fem.Function(W) - y_A, y_C, phi, p = split(u) - (v_A, v_C, v_1, v_2) = TestFunctions(W) + phi, p = split(u) + (v_1, v_2) = TestFunctions(W) # Collapse function space for bcs W0, _ = W.sub(0).collapse() W1, _ = W.sub(1).collapse() - W2, _ = W.sub(2).collapse() - W3, _ = W.sub(3).collapse() - + # Define boundary conditions values def phi_left_(x): return np.full_like(x[0], phi_left) @@ -164,42 +162,36 @@ def solve_System_2eq(phi_left:float, phi_right:float, p_right:float, z_A:float, return np.full_like(x[0], phi_right) def p_right_(x): return np.full_like(x[0], p_right) - def y_A_right_(x): - return np.full_like(x[0], y_A_R) - def y_C_right_(x): - return np.full_like(x[0], y_C_R) - + # Interpolate bcs functions - phi_left_bcs = fem.Function(W2) + phi_left_bcs = fem.Function(W0) phi_left_bcs.interpolate(phi_left_) - phi_right_bcs = fem.Function(W2) + phi_right_bcs = fem.Function(W0) phi_right_bcs.interpolate(phi_right_) - p_right_bcs = fem.Function(W3) + p_right_bcs = fem.Function(W1) p_right_bcs.interpolate(p_right_) - y_A_right_bcs = fem.Function(W0) - y_A_right_bcs.interpolate(y_A_right_) - y_C_right_bcs = fem.Function(W1) - y_C_right_bcs.interpolate(y_C_right_) - + # Identify dofs for boundary conditions # Define boundary conditions - facet_left_dofs = fem.locate_dofs_geometrical((W.sub(2), W.sub(2).collapse()[0]), Left) - facet_right_dofs = fem.locate_dofs_geometrical((W.sub(2), W.sub(2).collapse()[0]), Right) - bc_left_phi = fem.dirichletbc(phi_left_bcs, facet_left_dofs, W.sub(2)) - bc_right_phi = fem.dirichletbc(phi_right_bcs, facet_right_dofs, W.sub(2)) - - facet_right_dofs = fem.locate_dofs_geometrical((W.sub(3), W.sub(3).collapse()[0]), Right) - bc_right_p = fem.dirichletbc(p_right_bcs, facet_right_dofs, W.sub(3)) - + facet_left_dofs = fem.locate_dofs_geometrical((W.sub(0), W.sub(0).collapse()[0]), Left) facet_right_dofs = fem.locate_dofs_geometrical((W.sub(0), W.sub(0).collapse()[0]), Right) - bc_right_y_A = fem.dirichletbc(y_A_right_bcs, facet_right_dofs, W.sub(0)) + bc_left_phi = fem.dirichletbc(phi_left_bcs, facet_left_dofs, W.sub(0)) + bc_right_phi = fem.dirichletbc(phi_right_bcs, facet_right_dofs, W.sub(0)) facet_right_dofs = fem.locate_dofs_geometrical((W.sub(1), W.sub(1).collapse()[0]), Right) - bc_right_y_C = fem.dirichletbc(y_C_right_bcs, facet_right_dofs, W.sub(1)) + bc_right_p = fem.dirichletbc(p_right_bcs, facet_right_dofs, W.sub(1)) + # Combine boundary conditions into list - bcs = [bc_left_phi, bc_right_phi, bc_right_p, bc_right_y_A, bc_right_y_C] - + bcs = [bc_left_phi, bc_right_phi, bc_right_p] + + def y_A(phi, p): + D_A = y_A_R / exp(-(solvation + 1) * a2 * p_right - z_A * phi_right) + return D_A * exp(-(solvation + 1) * a2 * p - z_A * phi) + + def y_C(phi, p): + D_C = y_C_R / exp(-(solvation + 1) * a2 * p_right - z_C * phi_right) + return D_C * exp(-(solvation + 1) * a2 * p - z_C * phi) # Define variational problem @@ -207,30 +199,6 @@ def solve_System_2eq(phi_left:float, phi_right:float, p_right:float, z_A:float, # total free charge density def nF(y_A, y_C): return (z_C * y_C + z_A * y_A) - - # Diffusion fluxes for species A and C - def J_A(y_A, y_C, phi, p): - return ln(y_A) + a2 * (p - 1) * (solvation + 1) + z_A * phi - - def J_C(y_A, y_C, phi, p): - return ln(y_C) + a2 * (p - 1) * (solvation + 1) + z_C * phi - - # Variational Form - A = ( - inner(grad(phi), grad(v_1)) * dx - - 1 / Lambda2 * nF(y_A, y_C) * v_1 * dx - ) + ( - inner(grad(p), grad(v_2)) * dx - + 1 / a2 * nF(y_A, y_C) * dot(grad(phi), grad(v_2)) * dx - ) + ( - inner(grad(J_A(y_A, y_C, phi, p)), grad(v_A)) * dx - + inner(grad(J_C(y_A, y_C, phi, p)), grad(v_C)) * dx - ) - if PoissonBoltzmann: - A += ( - inner(grad(- a2 * (p - 1) * (solvation + 1)), grad(v_A)) * dx - + inner(grad(- a2 * (p - 1) * (solvation + 1)), grad(v_C)) * dx - ) else: # total number density def n(p): @@ -239,37 +207,16 @@ def solve_System_2eq(phi_left:float, phi_right:float, p_right:float, z_A:float, # total free charge density def nF(y_A, y_C, p): return (z_C * y_C + z_A * y_A) * n(p) - - # Diffusion fluxes for species A and C - def J_A(y_A, y_C, phi, p): - return ln(y_A) + a2 * (solvation + 1) * K * ln(1 + 1/K * (p-1)) + z_A * phi - - def J_C(y_A, y_C, phi, p): - return ln(y_C) + a2 * (solvation + 1)* K * ln(1 + 1/K * (p-1)) + z_C * phi - - A = ( - inner(grad(phi), grad(v_1)) * dx - - 1 / Lambda2 * nF(y_A, y_C, p) * v_1 * dx - ) + ( - inner(grad(p), grad(v_2)) * dx - + 1 / a2 * nF(y_A, y_C, p) * dot(grad(phi), grad(v_2)) * dx - ) + ( - inner(grad(J_A(y_A, y_C, phi, p)), grad(v_A)) * dx - + inner(grad(J_C(y_A, y_C, phi, p)), grad(v_C)) * dx - ) + # Variational Form + A = ( + inner(grad(phi), grad(v_1)) * dx + - 1 / Lambda2 * nF(y_A(phi, p), y_C(phi, p)) * v_1 * dx + ) + ( + inner(grad(p), grad(v_2)) * dx + + 1 / a2 * nF(y_A(phi, p), y_C(phi, p)) * dot(grad(phi), grad(v_2)) * dx + ) F = A - # Initialize initial guess for u - y_C_init = fem.Function(W1) - y_A_init = fem.Function(W0) - y_C_init.interpolate(lambda x: np.full_like(x[0], y_C_R)) - y_A_init.interpolate(lambda x: np.full_like(x[0], y_A_R)) - - with u.vector.localForm() as u_loc: - u_loc.set(0) - u.sub(0).interpolate(y_A_init) - u.sub(1).interpolate(y_C_init) - # Define Nonlinear Problem problem = NonlinearProblem(F, u, bcs=bcs) @@ -294,20 +241,22 @@ def solve_System_2eq(phi_left:float, phi_right:float, p_right:float, z_A:float, print(f"Number of interations: {n:d}") # Split the mixed function space into the individual components - y_A, y_C, phi, p = u.split() + phi, p = u.split() # Return the solution if return_type=='Vector': x_vals = np.array(msh.geometry.x[:,0]) - y_A_vals = np.array(u.sub(0).collapse().x.array) - y_C_vals = np.array(u.sub(1).collapse().x.array) - phi_vals = np.array(u.sub(2).collapse().x.array) - p_vals = np.array(u.sub(3).collapse().x.array) + phi_vals = np.array(u.sub(0).collapse().x.array) + p_vals = np.array(u.sub(1).collapse().x.array) + + # Calculate the atomic fractions + D_A = y_A_R / np.exp(-(solvation + 1) * a2 * p_right - z_A * phi_right) + y_A_vals = D_A * np.exp(-(solvation + 1) * a2 * p_vals - z_A * phi_vals) + + D_C = y_C_R / np.exp(-(solvation + 1) * a2 * p_right - z_C * phi_right) + y_C_vals = D_C * np.exp(-(solvation + 1) * a2 * p_vals - z_C * phi_vals) return y_A_vals, y_C_vals, phi_vals, p_vals, x_vals - elif return_type=='Scalar': - return y_A, y_C, phi, p, msh - if __name__ == '__main__': # Define the parameters diff --git a/src/Helper_DoubleLayerCapacity.py b/src/Helper_DoubleLayerCapacity.py index d8bd697..4da01eb 100644 --- a/src/Helper_DoubleLayerCapacity.py +++ b/src/Helper_DoubleLayerCapacity.py @@ -157,10 +157,15 @@ def Q_DL_dimless_ana(y_A_R:float, y_C_R:float, y_N_R:float, z_A:float, z_C:float D_C = y_C_R / (np.exp(-(solvation+1)*a2*p_R-z_C*phi_R)) D_N = y_N_R / (np.exp(-(solvation+1)*a2*p_R-z_N*phi_R)) E_p = p_R - CLambda_L = np.log(D_A * np.exp(-z_A * phi_L - (solvation+1) * E_p) + D_C * np.exp(-z_C * phi_L - (solvation+1) * E_p) + D_N * np.exp(-z_N * phi_L - (solvation+1) * E_p)) - CLambda_R = np.log(D_A * np.exp(-z_A * phi_R + E_p * a2) + D_C * np.exp(-z_C * phi_R + E_p * a2) + D_N * np.exp(-z_N * phi_R + E_p * a2)) + + CLambda_L = np.log(D_A * np.exp(-z_A * phi_L) + D_C * np.exp(-z_C * phi_L) + D_N * np.exp(-z_N * phi_L)) + CLambda_R = np.log(D_A * np.exp(-z_A * phi_R) + D_C * np.exp(-z_C * phi_R) + D_N * np.exp(-z_N * phi_R)) + + # dx_Phi_L = np.sqrt((CLambda_L - (solvation+1) * a2 * E_p) * 2 / (solvation + 1)) + # dx_Phi_R = np.sqrt((CLambda_R - (solvation+1) * a2 * E_p) * 2 / (solvation + 1)) + # Q_DL = Lambda2 * (dx_Phi_L - dx_Phi_R) Lambda = np.sqrt(Lambda2) - Q_DL = (phi_L-phi_R) / np.abs(phi_L-phi_R) * Lambda * np.sqrt(2/(solvation+1)) * (np.sqrt(CLambda_L) - np.sqrt(CLambda_R)) + Q_DL = (phi_L-phi_R) / np.abs(phi_L-phi_R) * Lambda * np.sqrt(2/(solvation+1)) * (np.sqrt(CLambda_L + E_p * a2) - np.sqrt(CLambda_R + E_p * a2)) else: C_A = y_A_R / ((K + p_R - 1)**(-(solvation+1)*a2*K)*np.exp(-z_A*phi_R)) C_C = y_C_R / ((K + p_R - 1)**(-(solvation+1)*a2*K)*np.exp(-z_C*phi_R)) diff --git a/src/__pycache__/Eq02.cpython-312.pyc b/src/__pycache__/Eq02.cpython-312.pyc index 0a43f005f31a0dc1160dbe3957614a55dd0419bd..99acdd38417e0f045265675a551c6f42a3df0a2d 100644 GIT binary patch delta 4920 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 BBKH6Q delta 8057 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 O$3D1lY>u-1!w3Mg6P%O) diff --git a/src/__pycache__/Helper_DoubleLayerCapacity.cpython-312.pyc b/src/__pycache__/Helper_DoubleLayerCapacity.cpython-312.pyc index 0520aeca0b59a17e6054ae07e36af289020f6477..ec8a8993fff2cb48986394c354c67b2ed42ec555 100644 GIT binary patch delta 1001 zcmZn<x*ouLnwOW0fq{YHU*+MnCmVTR@>*~f=jW6qmSpDVCG&t}pqPV!fq|KUf#LHe zb_Ry&3@MDYJT)9COxYR?3=<fOZqzVLzQ8ZP*^^J2adL%#;pC;lVw2DFYfgS5EHGI> zkaM!Kfahcd5#GtI0-BRA2=Pun&95<8Usz}|ui$x}8i5+FDpm%DDh39I$#(=bPy{x! z2ywD7I&9_>+slk_+GIx6)Xk<+sf>)QlV?jS)N`&-T*y5mZ$;2WIsFaH7v+pMIPYNH z=y_4j_OiI$MLzou<{Ny%Q;MdSO)OhLZMA|O%#2Sa^T~=iUsg1|C~dlfc~8>*w4G@O zk`IKOa6K4v*)Hg!MbJg*pbMc9SE6IDM<-s4PP~wub|ES4LT28@q`b?~`4>a;FG}ZM z7Acs_Ei36o1#>z$zi=@KiA>%uYg8|JAvolu^uhegc3~I!!>&ZdT#rh;7?pS-Df>cR z;f0d2i+N=ilgchfm0zi<`Od&l&FIAR5kh@nU~p#gVfw(p;Lj8UqC%O%L2~{O%9$yg z=`%=W1V}KN5ljh0GIg+gP-5T_Xz>0Z&%iHIKZ9|G^Gx;yhBJ9qXl`&^sl9@4L(xSI z%N>drHLNd++w2M2AGR~>K=1*V6QT#bF5CKF6!*W#7XXejYG~CZ$=c25<Zd%Ep5DwS z7R<EyuhM@;M55cwsLIXAxO+0Mnifm?W$uj4)@qX&T|5{W1!kCjU;xo8SRwR=PzZeh z%0B^>zYq}%k+_hW1!WYJeqivJET|!71v20R1A{L(q@kQ}rWhuO!Ve4#8BDndP7zZ% o(`T@yA3`Q4YV=6CFncn8U@&GB_`ty6#~jT31;j4mWnf?c0A06XOaK4? delta 1162 zcmcZ}&>qBlnwOW0fq{X+CgpG%&qm&tye6#Ki3J6T$vhxYDCT5fU|?ooVEFunoq=IG zLkeRpPYp*3Q?>>J!vw~npEV4V9r)!p`|?RM3e<3?Fjp}!Fw_WCu`)1JaZR=mFcxJ6 zu`y)w1k7>BT;osSsu8H+0%@yam~0_nfy~VlutDao5V*&-ni1sE$vT1xOf^EAj|p<J zFxqUsAi9?s;+e^TDyf@)OQte1)=y59R;XuPkhqY2M%D_UnYk;RFUsj}VBX-mk@KRQ z<z;cJi+t7{%s2RirxZ;un^?Ahx=IB*nD2;)O=gx6so%l6BXKAD0ppGJ7d6~2E4p8l zc0ZwfB_QZ}K-k5AunQ5<7s8`2#3f%0Pri_zb0IhHVtU?%qSA|Lr5D3XF9(!e^eVe3 zU3Q_e<_iOZ1CtZecM#>q^btaRU|{fI@@D$Lz~By{{Fs74a_&sQAi;2^7)KBt%M=F^ z4QGl2@sgNQLA)>s<<6AK^ciGY8c48;5lqRXfy6}9nL0S{OlFi3tKU$(BWPpAMGgDQ ziVhd09S#_u5jrn+QtU!t$c2EA3*j*r17a@3CtXNQzZjo>F(Cc2XU0Y6jEmA47qauN z6c%4EEWcP-exb7FLPgC7kX^1IyDAx7LC%4Cz>g^aB<KO5Dj5SnE+}FQ1PSJdKxmmj zkf3M~$XSBIllRIP8H%1LJSl!K`?6ioMgE{GVG-BEVlRfpUWiM-9F}n<JNJ5a(Z%ed z3&k~;vuiJg)n4SUo17pkS+BZ*ai#hSt_?;jc{e1k<i99xu}5gX*iNwn!UtFmgdXI& zZ0UVb-1{P*4>*a;V4UGRlYN2VOr90g(I|t@%FR-8w;36aZ@wTJ%rseFoOAMAC8^1_ z61<x$mCiFl()s2<6>dhxZIfeFwOEobbEj<XR-MG?;>^e>FvIi%1BhP13ZXZILg)if z{t2l3g@{;)#D&x>D5IeC1B3JAM0GJMkO3bU7<`$6m>_Z=7#PBtVi24Zrc4AUkExjH nGuYA(UXwSf_eeP~yE1=ZFlH3^z`)?e?9coK#4h4xU|;|M6R3R2 -- GitLab