From 4a21a43122b225f8fb699a1f4fb4bd814971afcc Mon Sep 17 00:00:00 2001 From: jshipton Date: Tue, 15 Aug 2023 14:33:50 +0100 Subject: [PATCH 01/48] rexi implementation --- gusto/__init__.py | 1 + gusto/rexi/__init__.py | 2 + gusto/rexi/rexi.py | 236 ++++++++++++++++++++++++++++++++ gusto/rexi/rexi_coefficients.py | 137 ++++++++++++++++++ 4 files changed, 376 insertions(+) create mode 100644 gusto/rexi/__init__.py create mode 100644 gusto/rexi/rexi.py create mode 100644 gusto/rexi/rexi_coefficients.py diff --git a/gusto/__init__.py b/gusto/__init__.py index 772e94656..4824b90f8 100644 --- a/gusto/__init__.py +++ b/gusto/__init__.py @@ -36,6 +36,7 @@ def perp(self, o, a): from gusto.physics import * # noqa from gusto.preconditioners import * # noqa from gusto.recovery import * # noqa +from gusto.rexi import * # noqa from gusto.spatial_methods import * # noqa from gusto.time_discretisation import * # noqa from gusto.timeloop import * # noqa diff --git a/gusto/rexi/__init__.py b/gusto/rexi/__init__.py new file mode 100644 index 000000000..3e5079311 --- /dev/null +++ b/gusto/rexi/__init__.py @@ -0,0 +1,2 @@ +from gusto.rexi.rexi import * # noqa +from gusto.rexi.rexi_coefficients import * # noqa diff --git a/gusto/rexi/rexi.py b/gusto/rexi/rexi.py new file mode 100644 index 000000000..6f3176df2 --- /dev/null +++ b/gusto/rexi/rexi.py @@ -0,0 +1,236 @@ +from gusto.rexi.rexi_coefficients import * +from firedrake import Function, TrialFunctions, TestFunctions, \ + Constant, DirichletBC, \ + LinearVariationalProblem, LinearVariationalSolver, MixedFunctionSpace +from gusto import Configuration, replace_subject, drop, time_derivative, all_terms, replace_test_function, prognostic, Term, perp, NullTerm, linearisation, subject, replace_trial_function +from firedrake.formmanipulation import split_form + + +class RexiParameters(Configuration): + """ + Parameters for the REXI coefficients + """ + h = 0.2 + M = 64 + reduce_to_half = False + + +class Rexi(object): + """ + Class defining the solver for the system + + (A_n + tau L)V_n = U + + required for computing the matrix exponential as described in notes.pdf + + :arg equation: :class:`.Equation` object defining the equation set to + be solved + :arg rexi_parameters: :class:`.Equation` object + :arg solver_parameters: dictionary of solver parameters. Default None, + which results in the default solver parameters defined in the equation + class being used. + :arg manager: :class:`.Ensemble` object containing the space and ensemble + subcommunicators + + """ + def __init__(self, equation, rexi_parameters, *, solver_parameters=None, + manager=None): + + residual = equation.residual.label_map( + lambda t: t.has_label(linearisation), + map_if_true=lambda t: Term(t.get(linearisation).form, t.labels), + map_if_false=drop) + residual = residual.label_map( + all_terms, + lambda t: replace_trial_function(t.get(subject))(t)) + + # Get the Rexi Coefficients, given the values of h and M in + # rexi_parameters + self.alpha, self.beta, self.beta2 = RexiCoefficients(rexi_parameters) + + self.manager = manager + + # define the start point of the solver loop (idx) and the + # number of solvers (N) for this process depending on the + # total number of solvers (nsolvers) and how many ensemble + # processes (neprocs) there are. + nsolvers = len(self.alpha) + if manager is None: + # if running in serial we loop over all the solvers, from + # 0: nsolvers + self.N = nsolvers + self.idx = 0 + else: + rank = manager.ensemble_comm.rank + neprocs = manager.ensemble_comm.size + m = int(nsolvers/neprocs) + p = nsolvers - m*neprocs + if rank < p: + self.N = m+1 + self.idx = rank*(m+1) + else: + self.N = m + self.idx = rank*m + p + + # set dummy constants for tau and A_i + self.ar = Constant(1.) + self.ai = Constant(1.) + self.tau = Constant(1.) + + # set up functions, problem and solver + W_ = equation.function_space + self.w_out = Function(W_) + spaces = [] + for i in range(len(W_)): + spaces.append(W_[i]) + spaces.append(W_[i]) + W = MixedFunctionSpace(spaces) + self.U0 = Function(W) + self.w_sum = Function(W) + self.w = Function(W) + self.w_ = Function(W) + tests = TestFunctions(W) + trials = TrialFunctions(W) + tests_r = tests[::2] + tests_i = tests[1::2] + trials_r = trials[::2] + trials_i = trials[1::2] + + ar, ai = self.ar, self.ai + a = NullTerm + L = NullTerm + for i in range(len(W_)): + ith_res = residual.label_map( + lambda t: t.get(prognostic) == equation.field_names[i], + lambda t: Term( + split_form(t.form)[i].form, + t.labels), + drop) + + mass_form = ith_res.label_map( + lambda t: t.has_label(time_derivative), + map_if_false=drop) + + m = mass_form.label_map( + all_terms, + replace_test_function(tests_r[i], i)) + a += ( + (ar + ai) * m.label_map(all_terms, + replace_subject(trials_r[i], i)) + + (ar - ai) * m.label_map(all_terms, + replace_subject(trials_i[i], i)) + ) + + L += ( + m.label_map(all_terms, replace_subject(self.U0.split()[2*i], i)) + + m.label_map(all_terms, replace_subject(self.U0.split()[2*i+1], i)) + ) + + m = mass_form.label_map( + all_terms, + replace_test_function(tests_i[i], i)) + a += ( + (ar - ai) * m.label_map(all_terms, + replace_subject(trials_r[i], i)) + +(-ar - ai) * m.label_map(all_terms, + replace_subject(trials_i[i], i)) + ) + + L += ( + m.label_map(all_terms, replace_subject(self.U0.split()[2*i], i)) + - m.label_map(all_terms, replace_subject(self.U0.split()[2*i+1], i)) + ) + + L_form = ith_res.label_map( + lambda t: t.has_label(time_derivative), + drop) + + Lr = L_form.label_map( + all_terms, + replace_test_function(tests_r[i], i)) + a -= self.tau * Lr.label_map(all_terms, + replace_subject(trials_r)) + a -= self.tau * Lr.label_map(all_terms, + replace_subject(trials_i)) + + Li = L_form.label_map( + all_terms, + replace_test_function(tests_i[i], i)) + a -= self.tau * Li.label_map(all_terms, + replace_subject(trials_r)) + a += self.tau * Li.label_map(all_terms, + replace_subject(trials_i)) + + + a = a.label_map(lambda t: t is NullTerm, drop) + L = L.label_map(lambda t: t is NullTerm, drop) + + if hasattr(equation, "aP"): + aP = equation.aP(trial, self.ai, self.tau) + else: + aP = None + + # Boundary conditions (assumes extruded mesh) + # BCs are declared for the plain velocity space. As we need them in + # extended mixed problem, we replicate the BCs but for subspace of W + bcs = [] + for bc in equation.bcs['u']: + bcs.append(DirichletBC(W.sub(0), bc.function_arg, bc.sub_domain)) + bcs.append(DirichletBC(W.sub(1), bc.function_arg, bc.sub_domain)) + + rexi_prob = LinearVariationalProblem(a.form, L.form, self.w, aP=aP, + bcs=bcs, + constant_jacobian=False) + + #if solver_parameters is None: + # solver_parameters = equation.solver_parameters + + self.solver = LinearVariationalSolver( + rexi_prob, solver_parameters=solver_parameters) + + def solve(self, x_out, x_in, dt): + """ + Solve method for approximating the matrix exponential by a + rational sum. Solves + + (A_n + tau L)V_n = U + + multiplies by the corresponding B_n and sums over n. + + :arg U0: the mixed function on the rhs. + :arg dt: the value of tau + + """ + + # assign tau and U0 and initialise solution to 0. + self.tau.assign(dt) + Uin = x_in.split() + U0 = self.U0.split() + for i in range(len(Uin)): + U0[2*i].assign(Uin[i]) + self.w_.assign(0.) + w_ = self.w_.split() + w = self.w.split() + + # loop over solvers, assigning a_i, solving and accumulating the sum + for i in range(self.N): + j = self.idx + i + self.ar.assign(self.alpha[j].real) + self.ai.assign(self.alpha[j].imag) + self.solver.solve() + for k in range(len(Uin)): + wk = w_[2*k] + wk += Constant(self.beta[j].real)*w[2*k] - Constant(self.beta[j].imag)*w[2*k+1] + + # in parallel we have to accumulate the sum over all processes + if self.manager is not None: + self.manager.allreduce(self.w_, self.w_sum) + else: + self.w_sum.assign(self.w_) + + w_sum = self.w_sum.split() + w_out = self.w_out.split() + for i in range(len(w_out)): + w_out[i].assign(w_sum[2*i]) + + x_out.assign(self.w_out) diff --git a/gusto/rexi/rexi_coefficients.py b/gusto/rexi/rexi_coefficients.py new file mode 100644 index 000000000..455467c0d --- /dev/null +++ b/gusto/rexi/rexi_coefficients.py @@ -0,0 +1,137 @@ +import numpy + + +class REXIParameters(object): + """ + mu and a coefficients from + "A high-order time-parallel scheme for solving wave propagation problems + via the direct construction of an approximate time-evolution operator", + Haut et.al. + """ + + mu = -4.315321510875024 + 1j*0 + L = 11 + a = [ + -1.0845749544592896e-7 + 1j*2.77075431662228e-8, + 1.858753344202957e-8 + 1j*-9.105375434750162e-7, + 3.6743713227243024e-6 + 1j*7.073284346322969e-7, + -2.7990058083347696e-6 + 1j*0.0000112564827639346, + 0.000014918577548849352 + 1j*-0.0000316278486761932, + -0.0010751767283285608 + 1j*-0.00047282220513073084, + 0.003816465653840016 + 1j*0.017839810396560574, + 0.12124105653274578 + 1j*-0.12327042473830248, + -0.9774980792734348 + 1j*-0.1877130220537587, + 1.3432866123333178 + 1j*3.2034715228495942, + 4.072408546157305 + 1j*-6.123755543580666, + -9.442699917778205 + 1j*0., + 4.072408620272648 + 1j*6.123755841848161, + 1.3432860877712938 + 1j*-3.2034712658530275, + -0.9774985292598916 + 1j*0.18771238018072134, + 0.1212417070363373 + 1j*0.12326987628935386, + 0.0038169724770333343 + 1j*-0.017839242222443888, + -0.0010756025812659208 + 1j*0.0004731874917343858, + 0.000014713754789095218 + 1j*0.000031358475831136815, + -2.659323898804944e-6 + 1j*-0.000011341571201752273, + 3.6970377676364553e-6 + 1j*-6.517457477594937e-7, + 3.883933649142257e-9 + 1j*9.128496023863376e-7, + -1.0816457995911385e-7 + 1j*-2.954309729192276e-8 + ] + + +def b_coefficients(h, M): + """ + Compute the b coefficients where + b_m = h^2 exp(imh) + """ + m = numpy.arange(-M, M+1) + return numpy.exp(h*h)*numpy.exp(-1j*m*h) + + +def RexiCoefficients(rexi_parameters): + """ + Compute the A_n and B_n coefficients in the REXI sum + + exp(ix) \approx sum_{n=0}^{P} B_n (ix + A_n) + + where P = 4N+1 if rexi_parameters.reduce_to_half is False else + 2N+1 if True. + + Returns 3 numpy arrays: + alpha contains the A_n coefficients + beta contains the B_n coefficients + beta2 contains zeros if rexi_parameters.reduce_to_half is False else + it contains the coefficients that multiply the conjugate terms + + :arg rexi_parameters: class containing the parameters (h, M and + reduce_to_half) necessary to compute the coefficients + + """ + + h = float(rexi_parameters.h) + M = int(float(rexi_parameters.M)) + + # get L, mu and the a coefficients + params = REXIParameters() + L = params.L + mu = params.mu + a = params.a + + # calculate the b coefficients + b = b_coefficients(h, M) + + # allocate arrays for alpha, beta_re and beta_im + N = M + L + alpha = numpy.zeros((2*N+1,), dtype=numpy.complex128) + beta_re = numpy.zeros((2*N+1,), dtype=numpy.complex128) + beta_im = numpy.zeros((2*N+1,), dtype=numpy.complex128) + + # compute alpha, beta_re and beta_im + for l in range(-L, L+1): + for m in range(-M, M+1): + n = l+m + alpha[n+N] = h*(mu + 1j*n) + beta_re[n+N] += b[m+M].real*h*a[l+L] + beta_im[n+N] += b[m+M].imag*h*a[l+L] + + # calculate conj(beta_re) and conj(beta_im), used to define A_n and B_n + beta_conj_re = numpy.conjugate(beta_re) + beta_conj_im = numpy.conjugate(beta_im) + + if rexi_parameters.reduce_to_half: + # If reducing the number of solvers to (nearly) half, as + # described in notes.pdf, we only need alpha_n for n \in [N, + # 2N]. We need to retain all the betas (due to the lack of + # symmetry coming from the numerical calculation of the a + # coefficients) but in order not to special case the Nth + # solver (where the solution is real hence equal to its + # conjugate) we must divide the Nth value of the betas by 2. + alpha = alpha[N:] + beta_re[N] /= 2. + beta_im[N] /= 2. + beta_conj_re[N] /= 2. + beta_conj_im[N] /= 2. + beta = numpy.concatenate( + (beta_re[N:] + 1j*beta_im[N:], + -beta_conj_re[N::-1] - 1j*beta_conj_im[N::-1]) + )/2 + # beta2 is the coefficient that multiplies the conjugate of + # the solution + beta2 = numpy.concatenate( + (beta_re[N::-1] + 1j*beta_im[N::-1], + -beta_conj_re[N:] - 1j*beta_conj_im[N:]) + )/2 + else: + beta = numpy.concatenate( + (beta_re + 1j*beta_im, + -beta_conj_re[::-1] - 1j*beta_conj_im[::-1]) + )/2 + # when not reducing the number of solvers we return zero here + # in order not to special case this option (i.e. we still + # calculate the conjugate of the solution but then multiply it + # by zero - you're doing (nearly) twice the amount of work necessary + # anyway!) + beta2 = numpy.zeros(len(beta)) + + alpha = numpy.concatenate((alpha, -alpha)) + + return alpha, beta, beta2 From da360ed04694c1d980d40e73cb65fe6f33d14cbc Mon Sep 17 00:00:00 2001 From: jshipton Date: Tue, 15 Aug 2023 14:56:40 +0100 Subject: [PATCH 02/48] test for rexi --- gusto/configuration.py | 1 + gusto/fml/form_manipulation_language.py | 3 +- integration-tests/rexi/test_linear_sw.py | 92 ++++++++++++++++++++++++ 3 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 integration-tests/rexi/test_linear_sw.py diff --git a/gusto/configuration.py b/gusto/configuration.py index 4bf3b8b2b..6cc9e2201 100644 --- a/gusto/configuration.py +++ b/gusto/configuration.py @@ -5,6 +5,7 @@ __all__ = [ + "Configuration", "IntegrateByParts", "TransportEquationType", "OutputParameters", "CompressibleParameters", "ShallowWaterParameters", "EmbeddedDGOptions", "RecoveryOptions", "SUPGOptions", diff --git a/gusto/fml/form_manipulation_language.py b/gusto/fml/form_manipulation_language.py index 062fddb04..012e68f26 100644 --- a/gusto/fml/form_manipulation_language.py +++ b/gusto/fml/form_manipulation_language.py @@ -6,7 +6,8 @@ from firedrake import Constant, Function -__all__ = ["Label", "Term", "LabelledForm", "identity", "drop", "all_terms", +__all__ = ["Label", "Term", "NullTerm", "LabelledForm", + "identity", "drop", "all_terms", "keep", "subject", "name"] # ---------------------------------------------------------------------------- # diff --git a/integration-tests/rexi/test_linear_sw.py b/integration-tests/rexi/test_linear_sw.py new file mode 100644 index 000000000..d359fa1e4 --- /dev/null +++ b/integration-tests/rexi/test_linear_sw.py @@ -0,0 +1,92 @@ +""" +This runs the wave scenario from Schreiber et al 2018 for the linear f-plane +shallow water equations and compares the REXI output to the Implicit Midpoint +output to confirm that REXI is correct +""" + +from os.path import join, abspath, dirname +from gusto import * +from gusto.rexi import * +from firedrake import (PeriodicUnitSquareMesh, SpatialCoordinate, Constant, sin, + cos, pi, norm, as_vector, Function, File) + + +def run_rexi_sw(tmpdir): + # timestepping parameters + dt = 0.001 + tmax = 0.1 + + # Domain + n = 20 + mesh = PeriodicUnitSquareMesh(n, n) + domain = Domain(mesh, dt, 'BDM', 1) + + # set up linear shallow water equations + H = 1 + f = 1. + g = 1 + parameters = ShallowWaterParameters(H=H, g=g) + eqns = LinearShallowWaterEquations(domain, parameters, fexpr=Constant(f)) + + # I/O + output = OutputParameters(dirname=str(tmpdir)+"/waves_sw", + log_level='INFO') + io = IO(domain, output) + + # Timestepper + stepper = Timestepper(eqns, ImplicitMidpoint(domain), io) + + # Initial conditions + x, y = SpatialCoordinate(mesh) + u0 = stepper.fields("u") + D0 = stepper.fields("D") + uexpr = as_vector([cos(8*pi*x)*cos(2*pi*y), cos(4*pi*x)*cos(4*pi*y)]) + Dexpr = sin(4*pi*x)*cos(2*pi*y) - 0.2*cos(4*pi*x)*sin(4*pi*y) + u0.project(uexpr) + D0.interpolate(Dexpr) + + # Compute implicit midpoint solution + stepper.run(t=0, tmax=tmax) + usoln = stepper.fields("u") + Dsoln = stepper.fields("D") + + # Compute exponential solution and write out + rexi_output = File(str(tmpdir)+"/waves_sw/rexi.pvd") + domain = Domain(mesh, dt, 'BDM', 1) + parameters = ShallowWaterParameters(H=H, g=g) + linearisation_map = lambda t: \ + t.get(prognostic) in ["u", "D"] \ + and (any(t.has_label(time_derivative, pressure_gradient, coriolis)) + or (t.get(prognostic) == "D" and t.has_label(transport))) + eqns = ShallowWaterEquations(domain, parameters, fexpr=Constant(f), + linearisation_map=linearisation_map) + + U_in = Function(eqns.function_space) + Uexpl = Function(eqns.function_space) + u, D = U_in.split() + u.project(uexpr) + D.interpolate(Dexpr) + rexi_output.write(u, D) + + rexi = Rexi(eqns, RexiParameters()) + rexi.solve(Uexpl, U_in, tmax) + + uexpl, Dexpl = Uexpl.split() + u.assign(uexpl) + D.assign(Dexpl) + rexi_output.write(u, D) + + return usoln, Dsoln, uexpl, Dexpl + + +def test_rexi_sw(tmpdir): + + dirname = str(tmpdir) + + usoln, Dsoln, uexpl, Dexpl = run_rexi_sw(dirname) + + uerror = norm(usoln - uexpl) / norm(usoln) + assert uerror < 0.04 + + Derror = norm(Dsoln - Dexpl) / norm(Dsoln) + assert Derror < 0.02 From 4a66c1a03ad9b6b33f1bcb704adfe7b43d166e23 Mon Sep 17 00:00:00 2001 From: nhartney Date: Tue, 15 Aug 2023 15:10:18 +0100 Subject: [PATCH 03/48] update logging and time stepper --- integration-tests/rexi/test_linear_sw.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/integration-tests/rexi/test_linear_sw.py b/integration-tests/rexi/test_linear_sw.py index d359fa1e4..9603fddcf 100644 --- a/integration-tests/rexi/test_linear_sw.py +++ b/integration-tests/rexi/test_linear_sw.py @@ -29,12 +29,11 @@ def run_rexi_sw(tmpdir): eqns = LinearShallowWaterEquations(domain, parameters, fexpr=Constant(f)) # I/O - output = OutputParameters(dirname=str(tmpdir)+"/waves_sw", - log_level='INFO') + output = OutputParameters(dirname=str(tmpdir)+"/waves_sw") io = IO(domain, output) # Timestepper - stepper = Timestepper(eqns, ImplicitMidpoint(domain), io) + stepper = Timestepper(eqns, TrapeziumRule(domain), io) # Initial conditions x, y = SpatialCoordinate(mesh) From f5598b558ac1e4035a3c1fc5e56a6254c5a273e0 Mon Sep 17 00:00:00 2001 From: nhartney Date: Tue, 15 Aug 2023 15:58:57 +0100 Subject: [PATCH 04/48] put all REXI parameters in one class called RexiParameters --- gusto/rexi/rexi.py | 9 --------- gusto/rexi/rexi_coefficients.py | 14 +++++++++----- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/gusto/rexi/rexi.py b/gusto/rexi/rexi.py index 6f3176df2..ce0a399fd 100644 --- a/gusto/rexi/rexi.py +++ b/gusto/rexi/rexi.py @@ -6,15 +6,6 @@ from firedrake.formmanipulation import split_form -class RexiParameters(Configuration): - """ - Parameters for the REXI coefficients - """ - h = 0.2 - M = 64 - reduce_to_half = False - - class Rexi(object): """ Class defining the solver for the system diff --git a/gusto/rexi/rexi_coefficients.py b/gusto/rexi/rexi_coefficients.py index 455467c0d..2c2327487 100644 --- a/gusto/rexi/rexi_coefficients.py +++ b/gusto/rexi/rexi_coefficients.py @@ -1,7 +1,8 @@ import numpy +from gusto import Configuration -class REXIParameters(object): +class RexiParameters(Configuration): """ mu and a coefficients from "A high-order time-parallel scheme for solving wave propagation problems @@ -9,7 +10,11 @@ class REXIParameters(object): Haut et.al. """ + h = 0.2 + M = 64 + reduce_to_half = False mu = -4.315321510875024 + 1j*0 + # The variables below this line should not be changed. L = 11 a = [ -1.0845749544592896e-7 + 1j*2.77075431662228e-8, @@ -71,10 +76,9 @@ def RexiCoefficients(rexi_parameters): M = int(float(rexi_parameters.M)) # get L, mu and the a coefficients - params = REXIParameters() - L = params.L - mu = params.mu - a = params.a + L = rexi_parameters.L + mu = rexi_parameters.mu + a = rexi_parameters.a # calculate the b coefficients b = b_coefficients(h, M) From b4914437674c628d2aa152b56e2af2e32d76d030 Mon Sep 17 00:00:00 2001 From: nhartney Date: Tue, 15 Aug 2023 15:59:30 +0100 Subject: [PATCH 05/48] test for steps of the REXI approximation --- .../rexi/test_rexi_approximations.py | 122 ++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 integration-tests/rexi/test_rexi_approximations.py diff --git a/integration-tests/rexi/test_rexi_approximations.py b/integration-tests/rexi/test_rexi_approximations.py new file mode 100644 index 000000000..9c8905e77 --- /dev/null +++ b/integration-tests/rexi/test_rexi_approximations.py @@ -0,0 +1,122 @@ +""" +This tests each component of the REXI approximation, and sequentially builds the +full REXI approximation. +""" + +from gusto.rexi import * +from firedrake import exp, sqrt, pi +import pytest + +params = RexiParameters() +mu = params.mu +L = params.L +a = params.a + + +def exactGaussian(x, h): + """ + An exact Gaussian, for comparison with approximations. + """ + return exp(-(x*x)/(4.0*h*h))/sqrt(4.0*pi) + + +def approx_exp_as_Gaussian(h, M, x): + """ + Approximation of the exponential as a sum of (exact) Gaussian functions. + """ + b = b_coefficients(h, M) + sum = 0 + for m in range(-M, M+1): + sum += b[m+M] * exactGaussian(x+ (m*h), h) + return sum + + +def approxGaussian(x, h): + """ + Approximation of Gaussian basis function as a sum of complex rational + functions. + """ + x /= h + sum = 0 + for t in range(0, len(a)): + l = t-L + sum += (a[t]/(1j*x + mu + 1j*l)).real + return sum + + +def approx_e_ix(x, h, M, use_Gaussian_approx): + """ + Approximation of e^(ix), either with Gaussians approximated as fractions + or with the full REXI approximation (writing the sum of Gaussians and the + sum of rational functions as a single sum). + """ + b = b_coefficients(h, M) + sum = 0 + if use_Gaussian_approx: + # this is REXI with Gaussians approximated as fractions + for m in range(-M, M+1): + sum += b[m+M] * approxGaussian(x+m*h, h) + else: + # this is the full REXI (combining the sums) + alpha, beta, beta2 = RexiCoefficients(params) + for n in range(len(alpha)): + denom = (1j*x + alpha[n]) + sum += beta[n] / denom + + return sum + +# ------------------------------------------------------------------------ # +# Test 1: This tests the first REXI approximation: the exponential as a sum of +# Gaussians. The Gaussians are exact. The test compares the linear combination +# of Gaussian functions (with b coefficients as weights) with the exact +# exponential, for scalars between 1 and 10. +# ------------------------------------------------------------------------ # + +def test_sum_of_gaussian_approx(): + h = 0.2 + M = 64 + for x in range(10): + exact = exp(1j*x) + approx = approx_exp_as_Gaussian(h, M, x) + assert abs(exact - approx) < 1e-14 + +# ------------------------------------------------------------------------ # +# Test 2: This tests the second step: the approximation of the Gaussians as a +# sum of complex fractions. It compares an exact Gaussian function with an +# approximation as a sum of rational functions. The sum of rational functions +# varies depending on the choice of 'original_constants'. +# ------------------------------------------------------------------------ # + +def test_gaussian_approx(): + h = 0.2 + for x in range(10): + exact = exactGaussian(x, h) + approx = approxGaussian(x, h) + assert abs(exact - approx) < 7.15344e-13 + +# ------------------------------------------------------------------------ # +# Test 3: This combines Tests 1 and 2 to compare the exact exponential with an +# approximation produced using a linear combination of Gaussian functions, which +# are themselves approximated by a sum of rational functions. +# ------------------------------------------------------------------------ # + +def test_exponential_approx(): + h = 0.2 + M = 64 + for x in range(-int(h*M)+1, int(h*M)): + exact = exp(1j*x) + approx = approx_e_ix(x, h, M, True) + assert abs(exact - approx) < 2.e-11 + +# ------------------------------------------------------------------------ # +# Test 3: This test the full REXI appromimation, i.e. the combination of steps +# 1 and 2 into a single sum. +# ------------------------------------------------------------------------ # + +def test_rexi_exponential_approx(): + h = 0.2 + M = 64 + for x in range(-int(h*M)+1, int(h*M)): + exact = exp(1j*x) + approx = approx_e_ix(x, h, M, False) + assert abs(exact - approx) < 2.e-11 From c8d9d3970b3f3efb733c717ea68204a2327162ee Mon Sep 17 00:00:00 2001 From: jshipton Date: Tue, 15 Aug 2023 17:12:05 +0100 Subject: [PATCH 06/48] fix rexi to work with recent replacement changes and replace .split() with .subfunctions --- gusto/rexi/rexi.py | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/gusto/rexi/rexi.py b/gusto/rexi/rexi.py index 6f3176df2..60d1b35b9 100644 --- a/gusto/rexi/rexi.py +++ b/gusto/rexi/rexi.py @@ -105,7 +105,7 @@ def __init__(self, equation, rexi_parameters, *, solver_parameters=None, lambda t: Term( split_form(t.form)[i].form, t.labels), - drop) + map_if_false=drop) mass_form = ith_res.label_map( lambda t: t.has_label(time_derivative), @@ -113,32 +113,32 @@ def __init__(self, equation, rexi_parameters, *, solver_parameters=None, m = mass_form.label_map( all_terms, - replace_test_function(tests_r[i], i)) + replace_test_function(tests_r[i])) a += ( (ar + ai) * m.label_map(all_terms, - replace_subject(trials_r[i], i)) + replace_subject(trials_r[i], old_idx=i)) + (ar - ai) * m.label_map(all_terms, - replace_subject(trials_i[i], i)) + replace_subject(trials_i[i], old_idx=i)) ) L += ( - m.label_map(all_terms, replace_subject(self.U0.split()[2*i], i)) - + m.label_map(all_terms, replace_subject(self.U0.split()[2*i+1], i)) + m.label_map(all_terms, replace_subject(self.U0.subfunctions[2*i], i)) + + m.label_map(all_terms, replace_subject(self.U0.subfunctions[2*i+1], old_idx=i)) ) m = mass_form.label_map( all_terms, - replace_test_function(tests_i[i], i)) + replace_test_function(tests_i[i])) a += ( (ar - ai) * m.label_map(all_terms, - replace_subject(trials_r[i], i)) + replace_subject(trials_r[i], old_idx=i)) +(-ar - ai) * m.label_map(all_terms, - replace_subject(trials_i[i], i)) + replace_subject(trials_i[i], old_idx=i)) ) L += ( - m.label_map(all_terms, replace_subject(self.U0.split()[2*i], i)) - - m.label_map(all_terms, replace_subject(self.U0.split()[2*i+1], i)) + m.label_map(all_terms, replace_subject(self.U0.subfunctions[2*i], i)) + - m.label_map(all_terms, replace_subject(self.U0.subfunctions[2*i+1], i)) ) L_form = ith_res.label_map( @@ -147,7 +147,7 @@ def __init__(self, equation, rexi_parameters, *, solver_parameters=None, Lr = L_form.label_map( all_terms, - replace_test_function(tests_r[i], i)) + replace_test_function(tests_r[i])) a -= self.tau * Lr.label_map(all_terms, replace_subject(trials_r)) a -= self.tau * Lr.label_map(all_terms, @@ -155,7 +155,7 @@ def __init__(self, equation, rexi_parameters, *, solver_parameters=None, Li = L_form.label_map( all_terms, - replace_test_function(tests_i[i], i)) + replace_test_function(tests_i[i])) a -= self.tau * Li.label_map(all_terms, replace_subject(trials_r)) a += self.tau * Li.label_map(all_terms, @@ -204,13 +204,13 @@ def solve(self, x_out, x_in, dt): # assign tau and U0 and initialise solution to 0. self.tau.assign(dt) - Uin = x_in.split() - U0 = self.U0.split() + Uin = x_in.subfunctions + U0 = self.U0.subfunctions for i in range(len(Uin)): U0[2*i].assign(Uin[i]) self.w_.assign(0.) - w_ = self.w_.split() - w = self.w.split() + w_ = self.w_.subfunctions + w = self.w.subfunctions # loop over solvers, assigning a_i, solving and accumulating the sum for i in range(self.N): @@ -228,8 +228,8 @@ def solve(self, x_out, x_in, dt): else: self.w_sum.assign(self.w_) - w_sum = self.w_sum.split() - w_out = self.w_out.split() + w_sum = self.w_sum.subfunctions + w_out = self.w_out.subfunctions for i in range(len(w_out)): w_out[i].assign(w_sum[2*i]) From 78bbef6c8363331655e61ffce96e83fbb91ae111 Mon Sep 17 00:00:00 2001 From: jshipton Date: Wed, 16 Aug 2023 10:06:42 +0100 Subject: [PATCH 07/48] fix lint --- gusto/rexi/rexi.py | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/gusto/rexi/rexi.py b/gusto/rexi/rexi.py index bae75170d..00f72ccea 100644 --- a/gusto/rexi/rexi.py +++ b/gusto/rexi/rexi.py @@ -2,7 +2,10 @@ from firedrake import Function, TrialFunctions, TestFunctions, \ Constant, DirichletBC, \ LinearVariationalProblem, LinearVariationalSolver, MixedFunctionSpace -from gusto import Configuration, replace_subject, drop, time_derivative, all_terms, replace_test_function, prognostic, Term, perp, NullTerm, linearisation, subject, replace_trial_function +from gusto import (replace_subject, drop, time_derivative, + all_terms, replace_test_function, prognostic, + Term, NullTerm, linearisation, subject, + replace_trial_function) from firedrake.formmanipulation import split_form @@ -109,8 +112,8 @@ def __init__(self, equation, rexi_parameters, *, solver_parameters=None, (ar + ai) * m.label_map(all_terms, replace_subject(trials_r[i], old_idx=i)) + (ar - ai) * m.label_map(all_terms, - replace_subject(trials_i[i], old_idx=i)) - ) + replace_subject(trials_i[i], old_idx=i)) + ) L += ( m.label_map(all_terms, replace_subject(self.U0.subfunctions[2*i], i)) @@ -123,13 +126,15 @@ def __init__(self, equation, rexi_parameters, *, solver_parameters=None, a += ( (ar - ai) * m.label_map(all_terms, replace_subject(trials_r[i], old_idx=i)) - +(-ar - ai) * m.label_map(all_terms, - replace_subject(trials_i[i], old_idx=i)) - ) + + (-ar - ai) * m.label_map(all_terms, + replace_subject(trials_i[i], old_idx=i)) + ) L += ( - m.label_map(all_terms, replace_subject(self.U0.subfunctions[2*i], i)) - - m.label_map(all_terms, replace_subject(self.U0.subfunctions[2*i+1], i)) + m.label_map(all_terms, + replace_subject(self.U0.subfunctions[2*i], i)) + - m.label_map(all_terms, + replace_subject(self.U0.subfunctions[2*i+1], i)) ) L_form = ith_res.label_map( @@ -142,16 +147,15 @@ def __init__(self, equation, rexi_parameters, *, solver_parameters=None, a -= self.tau * Lr.label_map(all_terms, replace_subject(trials_r)) a -= self.tau * Lr.label_map(all_terms, - replace_subject(trials_i)) + replace_subject(trials_i)) Li = L_form.label_map( all_terms, replace_test_function(tests_i[i])) a -= self.tau * Li.label_map(all_terms, - replace_subject(trials_r)) + replace_subject(trials_r)) a += self.tau * Li.label_map(all_terms, - replace_subject(trials_i)) - + replace_subject(trials_i)) a = a.label_map(lambda t: t is NullTerm, drop) L = L.label_map(lambda t: t is NullTerm, drop) @@ -173,7 +177,7 @@ def __init__(self, equation, rexi_parameters, *, solver_parameters=None, bcs=bcs, constant_jacobian=False) - #if solver_parameters is None: + # if solver_parameters is None: # solver_parameters = equation.solver_parameters self.solver = LinearVariationalSolver( From d9448ce7d9d9fdc44e8535198356dfc1c2c31ece Mon Sep 17 00:00:00 2001 From: jshipton Date: Wed, 16 Aug 2023 10:19:53 +0100 Subject: [PATCH 08/48] fix lint for one of the test files --- .../rexi/test_rexi_approximations.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/integration-tests/rexi/test_rexi_approximations.py b/integration-tests/rexi/test_rexi_approximations.py index 9c8905e77..1034495e1 100644 --- a/integration-tests/rexi/test_rexi_approximations.py +++ b/integration-tests/rexi/test_rexi_approximations.py @@ -5,7 +5,6 @@ from gusto.rexi import * from firedrake import exp, sqrt, pi -import pytest params = RexiParameters() mu = params.mu @@ -15,7 +14,7 @@ def exactGaussian(x, h): """ - An exact Gaussian, for comparison with approximations. + An exact Gaussian, for comparison with approximations. """ return exp(-(x*x)/(4.0*h*h))/sqrt(4.0*pi) @@ -27,7 +26,7 @@ def approx_exp_as_Gaussian(h, M, x): b = b_coefficients(h, M) sum = 0 for m in range(-M, M+1): - sum += b[m+M] * exactGaussian(x+ (m*h), h) + sum += b[m+M] * exactGaussian(x + (m * h), h) return sum @@ -69,14 +68,15 @@ def approx_e_ix(x, h, M, use_Gaussian_approx): # Test 1: This tests the first REXI approximation: the exponential as a sum of # Gaussians. The Gaussians are exact. The test compares the linear combination # of Gaussian functions (with b coefficients as weights) with the exact -# exponential, for scalars between 1 and 10. +# exponential, for scalars between 1 and 10. # ------------------------------------------------------------------------ # + def test_sum_of_gaussian_approx(): h = 0.2 M = 64 for x in range(10): - exact = exp(1j*x) + exact = exp(1j * x) approx = approx_exp_as_Gaussian(h, M, x) assert abs(exact - approx) < 1e-14 @@ -87,6 +87,7 @@ def test_sum_of_gaussian_approx(): # varies depending on the choice of 'original_constants'. # ------------------------------------------------------------------------ # + def test_gaussian_approx(): h = 0.2 for x in range(10): @@ -95,11 +96,12 @@ def test_gaussian_approx(): assert abs(exact - approx) < 7.15344e-13 # ------------------------------------------------------------------------ # -# Test 3: This combines Tests 1 and 2 to compare the exact exponential with an +# Test 3: This combines Tests 1 and 2 to compare the exact exponential with an # approximation produced using a linear combination of Gaussian functions, which # are themselves approximated by a sum of rational functions. # ------------------------------------------------------------------------ # + def test_exponential_approx(): h = 0.2 M = 64 @@ -110,9 +112,10 @@ def test_exponential_approx(): # ------------------------------------------------------------------------ # # Test 3: This test the full REXI appromimation, i.e. the combination of steps -# 1 and 2 into a single sum. +# 1 and 2 into a single sum. # ------------------------------------------------------------------------ # + def test_rexi_exponential_approx(): h = 0.2 M = 64 From b38192f8ace06a920ce9c5880e771f283cb76f46 Mon Sep 17 00:00:00 2001 From: nhartney Date: Wed, 16 Aug 2023 14:31:19 +0100 Subject: [PATCH 09/48] test for REXI using linear shallow water waves on the plane scenario --- integration-tests/rexi/test_rexi_waves.py | 104 ++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 integration-tests/rexi/test_rexi_waves.py diff --git a/integration-tests/rexi/test_rexi_waves.py b/integration-tests/rexi/test_rexi_waves.py new file mode 100644 index 000000000..760ef721d --- /dev/null +++ b/integration-tests/rexi/test_rexi_waves.py @@ -0,0 +1,104 @@ +""" +This runs the wave scenario from Schreiber, Peixoto, Haut and Wingate (2018) +for the linear f-plane shallow water equations using REXI. It checks the output +against a known checkpointed answer. +""" + +from os.path import join, abspath, dirname +from gusto import * +from firedrake import (PeriodicSquareMesh, SpatialCoordinate, Constant, sin, + cos, pi, as_vector) + +import numpy as np + + +def run_linear_sw_rexi_wave(tmpdir): + # Parameters + dt = 0.001 + tmax = 0.1 + H = 1 + wx = 2 + wy = 1 + g = 1 + + # Domain + mesh_name = 'linear_sw_mesh' + L = 1 + nx = ny = 16 + mesh = PeriodicSquareMesh(nx, ny, L, direction='both', name=mesh_name) + x, y = SpatialCoordinate(mesh) + domain = Domain(mesh, dt, 'BDM', 1) + + # Equation + parameters = ShallowWaterParameters(H=H, g=g) + fexpr = Constant(1) + eqns = LinearShallowWaterEquations(domain, parameters, fexpr=fexpr) + + # I/O + output = OutputParameters( + dirname=str(tmpdir)+"/linear_sw_rexi_wave", + dumpfreq=1, + ) + io = IO(domain, output) + + # ---------------------------------------------------------------------- # + # Initial conditions + # ---------------------------------------------------------------------- # + + eta = sin(2*pi*(x-L/2)*wx)*cos(2*pi*(y-L/2)*wy) - (1/5)*cos(2*pi*(x-L/2)*wx)*sin(4*pi*(y-L/2)*wy) + Dexpr = H + eta + + u = cos(4*pi*(x-L/2)*wx)*cos(2*pi*(y-L/2)*wy) + v = cos(2*pi*(x-L/2)*wx)*cos(4*pi*(y-L/2)*wy) + uexpr = as_vector([u, v]) + + U_in = Function(eqns.function_space, name="U_in") + Uexpl = Function(eqns.function_space, name="Uexpl") + u, D = U_in.split() + u.project(uexpr) + D.interpolate(Dexpr) + + # --------------------------------------------------------------------- # + # Run + # --------------------------------------------------------------------- # + + rexi_params = RexiParameters() + rexi = Rexi(eqns, rexi_params) + rexi.solve(Uexpl, U_in, dt) + + uexpl, Dexpl = Uexpl.split() + + # --------------------------------------------------------------------- # + # Checkpointing + # --------------------------------------------------------------------- # + + # State for checking checkpoints + checkpoint_name = 'linear_sw_wave_rexi_chkpt.h5' + new_path = join(abspath(dirname(__file__)), '..', f'data/{checkpoint_name}') + check_output = OutputParameters(dirname=tmpdir+"/linear_sw_rexi_wave", + checkpoint_pickup_filename=new_path) + check_mesh = pick_up_mesh(check_output, mesh_name) + check_domain = Domain(check_mesh, dt, 'BDM', 1) + check_eqn = ShallowWaterEquations(check_domain, parameters, fexpr=fexpr) + check_io = IO(check_domain, output=check_output) + check_stepper = Timestepper(check_eqn, RK4(check_domain), check_io) + check_stepper.io.pick_up_from_checkpoint(check_stepper.fields) + check_u = check_stepper.fields("u") + check_D = check_stepper.fields("D") + + return uexpl, Dexpl, check_u, check_D + + +def test_linear_sw_rexi_wave(tmpdir): + + dirname = str(tmpdir) + uexpl, Dexpl, check_u, check_D = run_linear_sw_rexi_wave(dirname) + + diff_array_u = uexpl.dat.data - check_u.dat.data + diff_array_D = Dexpl.dat.data - check_D.dat.data + u_error = np.linalg.norm(diff_array_u) / np.linalg.norm(check_u.dat.data) + D_error = np.linalg.norm(diff_array_D) / np.linalg.norm(check_D.dat.data) + + # Slack values chosen to be robust to different platforms + assert u_error < 1e-10, 'u values in REXI linear shallow water wave test do not match KGO values' + assert D_error < 1e-10, 'D values in REXI linear shallow water wave test do not match KGO values' From 916613d6e5cf156ff98307cce38c5850c37e513e Mon Sep 17 00:00:00 2001 From: nhartney Date: Thu, 17 Aug 2023 12:09:09 +0100 Subject: [PATCH 10/48] test_linear_sw.py does the linear sw wave test with REXI and compares the output to a known checkpointed answer --- .../data/linear_sw_wave_rexi_chkpt.h5 | Bin 0 -> 573612 bytes integration-tests/rexi/test_linear_sw.py | 91 ++++++++------- integration-tests/rexi/test_rexi_waves.py | 104 ------------------ 3 files changed, 45 insertions(+), 150 deletions(-) create mode 100644 integration-tests/data/linear_sw_wave_rexi_chkpt.h5 delete mode 100644 integration-tests/rexi/test_rexi_waves.py diff --git a/integration-tests/data/linear_sw_wave_rexi_chkpt.h5 b/integration-tests/data/linear_sw_wave_rexi_chkpt.h5 new file mode 100644 index 0000000000000000000000000000000000000000..a0e16a1ea87aca700085e9cecdb3fa504f6e618e GIT binary patch literal 573612 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 literal 0 HcmV?d00001 diff --git a/integration-tests/rexi/test_linear_sw.py b/integration-tests/rexi/test_linear_sw.py index 9603fddcf..c0e18bda8 100644 --- a/integration-tests/rexi/test_linear_sw.py +++ b/integration-tests/rexi/test_linear_sw.py @@ -1,79 +1,75 @@ """ This runs the wave scenario from Schreiber et al 2018 for the linear f-plane -shallow water equations and compares the REXI output to the Implicit Midpoint -output to confirm that REXI is correct +shallow water equations and compares the REXI output to a known checkpointed +answer to confirm that REXI is correct. """ from os.path import join, abspath, dirname from gusto import * from gusto.rexi import * from firedrake import (PeriodicUnitSquareMesh, SpatialCoordinate, Constant, sin, - cos, pi, norm, as_vector, Function, File) + cos, pi, as_vector, Function, File) + +import numpy as np def run_rexi_sw(tmpdir): - # timestepping parameters + # Parameters dt = 0.001 tmax = 0.1 + H = 1 + f = 1 + g = 1 # Domain + mesh_name = 'linear_sw_mesh' + L = 1 n = 20 - mesh = PeriodicUnitSquareMesh(n, n) + mesh = PeriodicUnitSquareMesh(n, n, name=mesh_name) domain = Domain(mesh, dt, 'BDM', 1) - # set up linear shallow water equations - H = 1 - f = 1. - g = 1 + # Equation parameters = ShallowWaterParameters(H=H, g=g) - eqns = LinearShallowWaterEquations(domain, parameters, fexpr=Constant(f)) - - # I/O - output = OutputParameters(dirname=str(tmpdir)+"/waves_sw") - io = IO(domain, output) + fexpr = Constant(f) + eqns = LinearShallowWaterEquations(domain, parameters, fexpr=fexpr) - # Timestepper - stepper = Timestepper(eqns, TrapeziumRule(domain), io) + # REXI output + rexi_output = File(str(tmpdir)+"/waves_sw/rexi.pvd") # Initial conditions x, y = SpatialCoordinate(mesh) - u0 = stepper.fields("u") - D0 = stepper.fields("D") - uexpr = as_vector([cos(8*pi*x)*cos(2*pi*y), cos(4*pi*x)*cos(4*pi*y)]) - Dexpr = sin(4*pi*x)*cos(2*pi*y) - 0.2*cos(4*pi*x)*sin(4*pi*y) - u0.project(uexpr) - D0.interpolate(Dexpr) - - # Compute implicit midpoint solution - stepper.run(t=0, tmax=tmax) - usoln = stepper.fields("u") - Dsoln = stepper.fields("D") - - # Compute exponential solution and write out - rexi_output = File(str(tmpdir)+"/waves_sw/rexi.pvd") - domain = Domain(mesh, dt, 'BDM', 1) - parameters = ShallowWaterParameters(H=H, g=g) - linearisation_map = lambda t: \ - t.get(prognostic) in ["u", "D"] \ - and (any(t.has_label(time_derivative, pressure_gradient, coriolis)) - or (t.get(prognostic) == "D" and t.has_label(transport))) - eqns = ShallowWaterEquations(domain, parameters, fexpr=Constant(f), - linearisation_map=linearisation_map) - - U_in = Function(eqns.function_space) - Uexpl = Function(eqns.function_space) + uexpr = as_vector([cos(8*pi*(x-L/2))*cos(2*pi*(y-L/2)), cos(4*pi*(x-L/2))*cos(4*pi*(y-L/2))]) + Dexpr = H + sin(4*pi*(x-L/2))*cos(2*pi*(y-L/2)) - 0.2*cos(4*pi*(x-L/2))*sin(4*pi*(y-L/2)) + + U_in = Function(eqns.function_space, name="U_in") + Uexpl = Function(eqns.function_space, name="Uexpl") u, D = U_in.split() u.project(uexpr) D.interpolate(Dexpr) rexi_output.write(u, D) + # Compute exponential solution and write it out rexi = Rexi(eqns, RexiParameters()) rexi.solve(Uexpl, U_in, tmax) uexpl, Dexpl = Uexpl.split() u.assign(uexpl) D.assign(Dexpl) - rexi_output.write(u, D) + rexi_output.write(u, D) + + # Checkpointing + checkpoint_name = 'linear_sw_wave_rexi_chkpt.h5' + new_path = join(abspath(dirname(__file__)), '..', f'data/{checkpoint_name}') + check_output = OutputParameters(dirname=tmpdir+"/linear_sw_wave", + checkpoint_pickup_filename=new_path) + check_mesh = pick_up_mesh(check_output, mesh_name) + check_domain = Domain(check_mesh, dt, 'BDM', 1) + check_eqn = ShallowWaterEquations(check_domain, parameters, fexpr=fexpr) + check_io = IO(check_domain, output=check_output) + check_stepper = Timestepper(check_eqn, RK4(check_domain), check_io) + check_stepper.io.pick_up_from_checkpoint(check_stepper.fields) + usoln = check_stepper.fields("u") + Dsoln = check_stepper.fields("D") return usoln, Dsoln, uexpl, Dexpl @@ -84,8 +80,11 @@ def test_rexi_sw(tmpdir): usoln, Dsoln, uexpl, Dexpl = run_rexi_sw(dirname) - uerror = norm(usoln - uexpl) / norm(usoln) - assert uerror < 0.04 + udiff_arr = uexpl.dat.data - usoln.dat.data + Ddiff_arr = Dexpl.dat.data - Dsoln.dat.data + + uerror = np.linalg.norm(udiff_arr) / np.linalg.norm(usoln.dat.data) + Derror = np.linalg.norm(Ddiff_arr) / np.linalg.norm(Dsoln.dat.data) - Derror = norm(Dsoln - Dexpl) / norm(Dsoln) - assert Derror < 0.02 + assert uerror < 0.04, 'u values in REXI linear shallow water wave test do not match KGO values' + assert Derror < 0.02, 'D values in REXI linear shallow water wave test do not match KGO values' diff --git a/integration-tests/rexi/test_rexi_waves.py b/integration-tests/rexi/test_rexi_waves.py deleted file mode 100644 index 760ef721d..000000000 --- a/integration-tests/rexi/test_rexi_waves.py +++ /dev/null @@ -1,104 +0,0 @@ -""" -This runs the wave scenario from Schreiber, Peixoto, Haut and Wingate (2018) -for the linear f-plane shallow water equations using REXI. It checks the output -against a known checkpointed answer. -""" - -from os.path import join, abspath, dirname -from gusto import * -from firedrake import (PeriodicSquareMesh, SpatialCoordinate, Constant, sin, - cos, pi, as_vector) - -import numpy as np - - -def run_linear_sw_rexi_wave(tmpdir): - # Parameters - dt = 0.001 - tmax = 0.1 - H = 1 - wx = 2 - wy = 1 - g = 1 - - # Domain - mesh_name = 'linear_sw_mesh' - L = 1 - nx = ny = 16 - mesh = PeriodicSquareMesh(nx, ny, L, direction='both', name=mesh_name) - x, y = SpatialCoordinate(mesh) - domain = Domain(mesh, dt, 'BDM', 1) - - # Equation - parameters = ShallowWaterParameters(H=H, g=g) - fexpr = Constant(1) - eqns = LinearShallowWaterEquations(domain, parameters, fexpr=fexpr) - - # I/O - output = OutputParameters( - dirname=str(tmpdir)+"/linear_sw_rexi_wave", - dumpfreq=1, - ) - io = IO(domain, output) - - # ---------------------------------------------------------------------- # - # Initial conditions - # ---------------------------------------------------------------------- # - - eta = sin(2*pi*(x-L/2)*wx)*cos(2*pi*(y-L/2)*wy) - (1/5)*cos(2*pi*(x-L/2)*wx)*sin(4*pi*(y-L/2)*wy) - Dexpr = H + eta - - u = cos(4*pi*(x-L/2)*wx)*cos(2*pi*(y-L/2)*wy) - v = cos(2*pi*(x-L/2)*wx)*cos(4*pi*(y-L/2)*wy) - uexpr = as_vector([u, v]) - - U_in = Function(eqns.function_space, name="U_in") - Uexpl = Function(eqns.function_space, name="Uexpl") - u, D = U_in.split() - u.project(uexpr) - D.interpolate(Dexpr) - - # --------------------------------------------------------------------- # - # Run - # --------------------------------------------------------------------- # - - rexi_params = RexiParameters() - rexi = Rexi(eqns, rexi_params) - rexi.solve(Uexpl, U_in, dt) - - uexpl, Dexpl = Uexpl.split() - - # --------------------------------------------------------------------- # - # Checkpointing - # --------------------------------------------------------------------- # - - # State for checking checkpoints - checkpoint_name = 'linear_sw_wave_rexi_chkpt.h5' - new_path = join(abspath(dirname(__file__)), '..', f'data/{checkpoint_name}') - check_output = OutputParameters(dirname=tmpdir+"/linear_sw_rexi_wave", - checkpoint_pickup_filename=new_path) - check_mesh = pick_up_mesh(check_output, mesh_name) - check_domain = Domain(check_mesh, dt, 'BDM', 1) - check_eqn = ShallowWaterEquations(check_domain, parameters, fexpr=fexpr) - check_io = IO(check_domain, output=check_output) - check_stepper = Timestepper(check_eqn, RK4(check_domain), check_io) - check_stepper.io.pick_up_from_checkpoint(check_stepper.fields) - check_u = check_stepper.fields("u") - check_D = check_stepper.fields("D") - - return uexpl, Dexpl, check_u, check_D - - -def test_linear_sw_rexi_wave(tmpdir): - - dirname = str(tmpdir) - uexpl, Dexpl, check_u, check_D = run_linear_sw_rexi_wave(dirname) - - diff_array_u = uexpl.dat.data - check_u.dat.data - diff_array_D = Dexpl.dat.data - check_D.dat.data - u_error = np.linalg.norm(diff_array_u) / np.linalg.norm(check_u.dat.data) - D_error = np.linalg.norm(diff_array_D) / np.linalg.norm(check_D.dat.data) - - # Slack values chosen to be robust to different platforms - assert u_error < 1e-10, 'u values in REXI linear shallow water wave test do not match KGO values' - assert D_error < 1e-10, 'D values in REXI linear shallow water wave test do not match KGO values' From eae94302c63641212d4ee2d97e972c8769e71fd3 Mon Sep 17 00:00:00 2001 From: jshipton Date: Thu, 17 Aug 2023 13:21:13 +0100 Subject: [PATCH 11/48] fix firedrake split warning --- integration-tests/rexi/test_linear_sw.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/integration-tests/rexi/test_linear_sw.py b/integration-tests/rexi/test_linear_sw.py index 9603fddcf..bd68005d9 100644 --- a/integration-tests/rexi/test_linear_sw.py +++ b/integration-tests/rexi/test_linear_sw.py @@ -62,7 +62,7 @@ def run_rexi_sw(tmpdir): U_in = Function(eqns.function_space) Uexpl = Function(eqns.function_space) - u, D = U_in.split() + u, D = U_in.subfunctions u.project(uexpr) D.interpolate(Dexpr) rexi_output.write(u, D) @@ -70,7 +70,7 @@ def run_rexi_sw(tmpdir): rexi = Rexi(eqns, RexiParameters()) rexi.solve(Uexpl, U_in, tmax) - uexpl, Dexpl = Uexpl.split() + uexpl, Dexpl = Uexpl.subfunctions u.assign(uexpl) D.assign(Dexpl) rexi_output.write(u, D) From 29cf5935237168f672334363170ce2a5a97a3589 Mon Sep 17 00:00:00 2001 From: jshipton Date: Wed, 20 Mar 2024 15:30:09 +0000 Subject: [PATCH 12/48] fix definition of NullTerm --- gusto/rexi/rexi.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/gusto/rexi/rexi.py b/gusto/rexi/rexi.py index 00f72ccea..59b9aa6e3 100644 --- a/gusto/rexi/rexi.py +++ b/gusto/rexi/rexi.py @@ -2,13 +2,17 @@ from firedrake import Function, TrialFunctions, TestFunctions, \ Constant, DirichletBC, \ LinearVariationalProblem, LinearVariationalSolver, MixedFunctionSpace -from gusto import (replace_subject, drop, time_derivative, - all_terms, replace_test_function, prognostic, - Term, NullTerm, linearisation, subject, - replace_trial_function) +from gusto.labels import time_derivative, prognostic, linearisation +from firedrake.fml import ( + Term, all_terms, keep, drop, Label, subject, name_label, + replace_subject, replace_test_function, replace_trial_function +) from firedrake.formmanipulation import split_form +NullTerm = Term(None) + + class Rexi(object): """ Class defining the solver for the system From 8a935c7064296bfc5040f8dbe241446f1bb24e91 Mon Sep 17 00:00:00 2001 From: jshipton Date: Wed, 20 Mar 2024 15:34:36 +0000 Subject: [PATCH 13/48] argh lint! --- gusto/rexi/rexi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gusto/rexi/rexi.py b/gusto/rexi/rexi.py index 59b9aa6e3..c257b6886 100644 --- a/gusto/rexi/rexi.py +++ b/gusto/rexi/rexi.py @@ -4,7 +4,7 @@ LinearVariationalProblem, LinearVariationalSolver, MixedFunctionSpace from gusto.labels import time_derivative, prognostic, linearisation from firedrake.fml import ( - Term, all_terms, keep, drop, Label, subject, name_label, + Term, all_terms, drop, subject, replace_subject, replace_test_function, replace_trial_function ) from firedrake.formmanipulation import split_form From 0be451e1dbdff831152d181fec5e8c19a30c86b1 Mon Sep 17 00:00:00 2001 From: jshipton Date: Wed, 20 Mar 2024 16:30:29 +0000 Subject: [PATCH 14/48] still failing --- integration-tests/rexi/test_linear_sw.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/integration-tests/rexi/test_linear_sw.py b/integration-tests/rexi/test_linear_sw.py index 38e83f4d8..fd98ad027 100644 --- a/integration-tests/rexi/test_linear_sw.py +++ b/integration-tests/rexi/test_linear_sw.py @@ -62,7 +62,8 @@ def run_rexi_sw(tmpdir): checkpoint_name = 'linear_sw_wave_rexi_chkpt.h5' new_path = join(abspath(dirname(__file__)), '..', f'data/{checkpoint_name}') check_output = OutputParameters(dirname=tmpdir+"/linear_sw_wave", - checkpoint_pickup_filename=new_path) + checkpoint_pickup_filename=new_path, + checkpoint=True) check_mesh = pick_up_mesh(check_output, mesh_name) check_domain = Domain(check_mesh, dt, 'BDM', 1) check_eqn = ShallowWaterEquations(check_domain, parameters, fexpr=fexpr) From 759c673283c329579128ce733d524b68674ba8b7 Mon Sep 17 00:00:00 2001 From: jshipton Date: Thu, 21 Mar 2024 09:22:13 +0000 Subject: [PATCH 15/48] allow transporting velocity to be indexed --- gusto/labels.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gusto/labels.py b/gusto/labels.py index ec0fb4af4..64eda2db3 100644 --- a/gusto/labels.py +++ b/gusto/labels.py @@ -94,7 +94,7 @@ def __call__(self, target, value=None): explicit = Label("explicit") transport = Label("transport", validator=lambda value: type(value) == TransportEquationType) diffusion = Label("diffusion") -transporting_velocity = Label("transporting_velocity", validator=lambda value: type(value) in [Function, ufl.tensors.ListTensor]) +transporting_velocity = Label("transporting_velocity", validator=lambda value: type(value) in [Function, ufl.tensors.ListTensor, ufl.indexed.Indexed]) prognostic = Label("prognostic", validator=lambda value: type(value) == str) pressure_gradient = DynamicsLabel("pressure_gradient") coriolis = DynamicsLabel("coriolis") From f51aefa44e2e6fb4153f7c9880433a47ee5911d5 Mon Sep 17 00:00:00 2001 From: jshipton Date: Thu, 21 Mar 2024 21:09:53 +0000 Subject: [PATCH 16/48] some hacking for linear boussinesq equations --- gusto/equations.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/gusto/equations.py b/gusto/equations.py index 6d13b70a3..b5af2b2e5 100644 --- a/gusto/equations.py +++ b/gusto/equations.py @@ -1312,8 +1312,10 @@ def __init__(self, domain, parameters, # Don't include active tracers linearisation_map = lambda t: \ t.get(prognostic) in ['u', 'p', 'b'] \ - and (t.has_label(time_derivative) - or (t.get(prognostic) not in ['u', 'p'] and t.has_label(transport))) + and ( + any(t.has_label(time_derivative, pressure_gradient)) + or t.get(name_label) in ["sound", "gravity"] + or (t.get(prognostic) not in ['u'] and t.has_label(transport))) super().__init__(field_names, domain, space_names, linearisation_map=linearisation_map, @@ -1325,7 +1327,7 @@ def __init__(self, domain, parameters, w, phi, gamma = self.tests[0:3] u, p, b = split(self.X) - u_trial = split(self.trials)[0] + u_trial, p_trial, b_trial = split(self.trials) _, p_bar, b_bar = split(self.X_ref) # -------------------------------------------------------------------- # @@ -1370,12 +1372,14 @@ def __init__(self, domain, parameters, # -------------------------------------------------------------------- # # Pressure Gradient Term # -------------------------------------------------------------------- # - pressure_gradient_form = subject(prognostic(-div(w)*p*dx, 'u'), self.X) + linear_pg = pressure_gradient(subject(prognostic(-div(w)*p_trial*dx, 'u'), self.X)) + pressure_gradient_form = linearisation(pressure_gradient(subject(prognostic(-div(w)*p*dx, 'u'), self.X)), linear_pg) # -------------------------------------------------------------------- # # Gravitational Term # -------------------------------------------------------------------- # - gravity_form = subject(prognostic(-b*inner(w, domain.k)*dx, 'u'), self.X) + linear_gravity = name_label(subject(prognostic(-b_trial*inner(w, domain.k)*dx, 'u'), self.X), "gravity") + gravity_form = linearisation(name_label(subject(prognostic(-b*inner(w, domain.k)*dx, 'u'), self.X), "gravity"), linear_gravity) # -------------------------------------------------------------------- # # Divergence Term @@ -1383,8 +1387,12 @@ def __init__(self, domain, parameters, if compressible: cs = parameters.cs - divergence_form = subject( - prognostic(cs**2 * phi * div(u) * dx, 'p'), self.X) + linear_div_form = subject( + prognostic(cs**2 * phi * div(u_trial) * dx, 'p'), self.X) + divergence_form = linearisation( + name_label(subject( + prognostic(cs**2 * phi * div(u) * dx, 'p'), + self.X), "sound"), linear_div_form) else: # This enforces that div(u) = 0 # The p features here so that the div(u) evaluated in the From abe6229bfc08b86c674fe0929b725dd8d2535a19 Mon Sep 17 00:00:00 2001 From: jshipton Date: Tue, 2 Apr 2024 11:18:04 +0100 Subject: [PATCH 17/48] fix to default linearisation for Boussinesq --- gusto/equations.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/gusto/equations.py b/gusto/equations.py index 8357f3084..839e30aff 100644 --- a/gusto/equations.py +++ b/gusto/equations.py @@ -1491,13 +1491,14 @@ def __init__(self, domain, parameters, active_tracers = [] if linearisation_map == 'default': - # Default linearisation is time derivatives and scalar transport terms + # Default linearisation is time derivatives, scalar transport, + # pressure gradiant, gravity and divergence terms # Don't include active tracers linearisation_map = lambda t: \ t.get(prognostic) in ['u', 'p', 'b'] \ and ( - any(t.has_label(time_derivative, pressure_gradient)) - or t.get(name_label) in ["sound", "gravity"] + any(t.has_label(time_derivative, pressure_gradient, + divergence, gravity)) or (t.get(prognostic) not in ['u'] and t.has_label(transport))) super().__init__(field_names, domain, space_names, From 91fc87f7f049487b03c8732073f2950824110d0a Mon Sep 17 00:00:00 2001 From: jshipton Date: Tue, 2 Apr 2024 21:54:02 +0100 Subject: [PATCH 18/48] start of compressible boussinesq test for rexi --- .../test_linear_compressible_boussinesq.py | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 integration-tests/rexi/test_linear_compressible_boussinesq.py diff --git a/integration-tests/rexi/test_linear_compressible_boussinesq.py b/integration-tests/rexi/test_linear_compressible_boussinesq.py new file mode 100644 index 000000000..39485428d --- /dev/null +++ b/integration-tests/rexi/test_linear_compressible_boussinesq.py @@ -0,0 +1,106 @@ +""" +The gravity wave test case of Skamarock and Klemp (1994), solved using the +compressible Boussinesq equations. + +Buoyancy is transported using SUPG. +""" + +from gusto import * +from firedrake import (PeriodicIntervalMesh, ExtrudedMesh, + sin, SpatialCoordinate, Function, pi) +from firedrake.output import VTKFile + +def run_rexi_linear_boussinesq(tmpdir): + + # ---------------------------------------------------------------------- # + # Set up model objects + # ---------------------------------------------------------------------- # + t_max = 1000 + dt = t_max + L = 3.0e5 # Domain length + H = 1.0e4 # Height position of the model top + + tmax = 3600. + columns = 300 # number of columns + nlayers = 10 # horizontal layers + + # Domain + m = PeriodicIntervalMesh(columns, L) + mesh = ExtrudedMesh(m, layers=nlayers, layer_height=H/nlayers) + domain = Domain(mesh, dt, 'CG', 1) + + # Equation + parameters = BoussinesqParameters(cs=300) + eqns = LinearBoussinesqEquations(domain, parameters) + + # ---------------------------------------------------------------------- # + # Initial conditions + # ---------------------------------------------------------------------- # + + U_in = Function(eqns.function_space, name="U_in") + U_expl = Function(eqns.function_space, name="Uexpl") + u, p, b = U_in.subfunctions + + # spaces + Vb = b.function_space() + Vp = p.function_space() + + x, z = SpatialCoordinate(mesh) + + # first setup the background buoyancy profile + # z.grad(bref) = N**2 + N = parameters.N + bref = z*(N**2) + # interpolate the expression to the function + b_b = Function(Vb).interpolate(bref) + + # setup constants + a = 5.0e3 + deltab = 1.0e-2 + b_pert = deltab*sin(pi*z/H)/(1 + (x - L/2)**2/a**2) + # interpolate the expression to the function + b.interpolate(b_b + b_pert) + + p_b = Function(Vp) + boussinesq_hydrostatic_balance(eqns, b_b, p_b) + p.assign(p_b) + + # set the background buoyancy and pressure + _, p_ref, b_ref = eqns.X_ref.subfunctions + p_ref.assign(p_b) + b_ref.assign(b_b) + + # ----------------------------------------------------------------------- # + # Compute exponential solution + # ----------------------------------------------------------------------- # + # REXI output + rexi_output = VTKFile(str(tmpdir)+"/sk_wave/rexi.pvd") + u1, p1, b1 = U_expl.subfunctions + u1.assign(u1) + p1.assign(p1-p_b) + b1.assign(b1-b_b) + rexi_output.write(u1, p1, b1) + rexi = Rexi(eqns, RexiParameters()) + rexi.solve(U_expl, U_in, t_max) + u1, p1, b1 = U_expl.subfunctions + p1 -= p_b + b1 -= b_b + rexi_output.write(u1, p1, b1) + + return u1, p1, b1 + + +def test_rexi_linear_boussinesq(tmpdir): + + dirname = str(tmpdir) + u, p, b = run_rexi_linear_boussinesq(dirname) + + for variable in ['u', 'b', 'p']: + new_variable = stepper.fields(variable) + check_variable = check_stepper.fields(variable) + diff_array = new_variable.dat.data - check_variable.dat.data + error = np.linalg.norm(diff_array) / np.linalg.norm(check_variable.dat.data) + + # Slack values chosen to be robust to different platforms + assert error < 1e-10, f'Values for {variable} in ' + \ + 'Incompressible test do not match KGO values' From d7de3210c84077f8b7334dc86965b5ac8d5536e9 Mon Sep 17 00:00:00 2001 From: jshipton Date: Tue, 2 Apr 2024 22:12:07 +0100 Subject: [PATCH 19/48] fix lint and test description --- .../rexi/test_linear_compressible_boussinesq.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/integration-tests/rexi/test_linear_compressible_boussinesq.py b/integration-tests/rexi/test_linear_compressible_boussinesq.py index 39485428d..da381109e 100644 --- a/integration-tests/rexi/test_linear_compressible_boussinesq.py +++ b/integration-tests/rexi/test_linear_compressible_boussinesq.py @@ -1,8 +1,7 @@ """ -The gravity wave test case of Skamarock and Klemp (1994), solved using the -compressible Boussinesq equations. +Linear gravity wave test case of Skamarock and Klemp (1994), solved using +REXI applied to the compressible Boussinesq equations. -Buoyancy is transported using SUPG. """ from gusto import * @@ -10,6 +9,7 @@ sin, SpatialCoordinate, Function, pi) from firedrake.output import VTKFile + def run_rexi_linear_boussinesq(tmpdir): # ---------------------------------------------------------------------- # @@ -20,7 +20,6 @@ def run_rexi_linear_boussinesq(tmpdir): L = 3.0e5 # Domain length H = 1.0e4 # Height position of the model top - tmax = 3600. columns = 300 # number of columns nlayers = 10 # horizontal layers From 5772d981c3153de73f6f7c5ed33dc4553dc3de0a Mon Sep 17 00:00:00 2001 From: jshipton Date: Wed, 3 Apr 2024 11:25:22 +0100 Subject: [PATCH 20/48] File -> VTKFile --- integration-tests/rexi/test_linear_sw.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/integration-tests/rexi/test_linear_sw.py b/integration-tests/rexi/test_linear_sw.py index fd98ad027..4e2e17018 100644 --- a/integration-tests/rexi/test_linear_sw.py +++ b/integration-tests/rexi/test_linear_sw.py @@ -8,7 +8,8 @@ from gusto import * from gusto.rexi import * from firedrake import (PeriodicUnitSquareMesh, SpatialCoordinate, Constant, sin, - cos, pi, as_vector, Function, File) + cos, pi, as_vector, Function) +from firedrake.output import VTKFile import numpy as np @@ -34,7 +35,7 @@ def run_rexi_sw(tmpdir): eqns = LinearShallowWaterEquations(domain, parameters, fexpr=fexpr) # REXI output - rexi_output = File(str(tmpdir)+"/waves_sw/rexi.pvd") + rexi_output = VTKFile(str(tmpdir)+"/waves_sw/rexi.pvd") # Initial conditions x, y = SpatialCoordinate(mesh) From e558533f82a75d1cd5a8bce5ceed84e544f01aaf Mon Sep 17 00:00:00 2001 From: jshipton Date: Thu, 4 Apr 2024 16:44:27 +0100 Subject: [PATCH 21/48] improve docstring for rexi --- gusto/rexi/rexi.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/gusto/rexi/rexi.py b/gusto/rexi/rexi.py index c257b6886..2cf7b2ae5 100644 --- a/gusto/rexi/rexi.py +++ b/gusto/rexi/rexi.py @@ -19,21 +19,22 @@ class Rexi(object): (A_n + tau L)V_n = U - required for computing the matrix exponential as described in notes.pdf - - :arg equation: :class:`.Equation` object defining the equation set to - be solved - :arg rexi_parameters: :class:`.Equation` object - :arg solver_parameters: dictionary of solver parameters. Default None, - which results in the default solver parameters defined in the equation - class being used. - :arg manager: :class:`.Ensemble` object containing the space and ensemble - subcommunicators - + required for computing the matrix exponential. """ + def __init__(self, equation, rexi_parameters, *, solver_parameters=None, manager=None): + """ + Args: + equation (:class:`PrognosticEquation`): the model's equation + rexi_parameters (:class:`RexiParameters`): Rexi configuration + parameters + solver_parameters (dict, optional): dictionary of parameters to + pass to the solver. Defaults to None. + manager (:class:`.Ensemble`): the space and ensemble sub- + communicators. Defaults to None. + """ residual = equation.residual.label_map( lambda t: t.has_label(linearisation), map_if_true=lambda t: Term(t.get(linearisation).form, t.labels), From 03d6746a4b6aea6496a5e80961032530682f0df1 Mon Sep 17 00:00:00 2001 From: jshipton Date: Wed, 17 Apr 2024 19:55:37 +0100 Subject: [PATCH 22/48] update kgo file for rexi shallow water test --- .../data/linear_sw_wave_rexi_chkpt.h5 | Bin 573612 -> 580789 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/integration-tests/data/linear_sw_wave_rexi_chkpt.h5 b/integration-tests/data/linear_sw_wave_rexi_chkpt.h5 index a0e16a1ea87aca700085e9cecdb3fa504f6e618e..da948670ce834d1127e551f4f4c08cdc18ff339b 100644 GIT binary patch delta 130602 zcmbTf2Urv97xharp(|kTfDNpGVsEd#q2f`*-o-Ac2zFy{D0au*#fAztY&a-(QNV&C z(nPUiL9y%IYqEnJ&;Ps6cQ4QRvG&@TOlIEo&SXe(V(6^$$4`}CUP*iW(TXbdG#2u> z45^6ZD4EDE`^>9LqjV+_;~Gl&wQ}e#3yW1z+5rXZOzceXbJSE7ld-Etn+kh(7dIC- zYIwG7*+pC7Q?|pluM@e@;N6{Gi}PQRpC;Nri?N4s>^^zFlLYQ}@i$`-h~qwyl`uEbX*b_s&$is<(xm_S4=~V=U~gckk1x zOI$jjl}BVqWl9|nH8j$AT}H`W=c{~%)^e;f+R-FppqT}Y*wSTM3nve2oa~# zz=VMlM)>)v4gU$_2Tm9$pWmWsTHDm5I4?Y>o?*v~yLW*7jRuBJTV77PSGyv-j5P)x zKRLT$+gCC;ljwv0Udt=?)?tReailZ2Fg}{KrC|kCtyhH4kX?jiVfUwHxM|f?(X>cD z6T<^*Ow3I3QEo$g#!eV6541HeCC?C2$+Z@eO+mwG{|SmFmZOE#%qqynup;sU{mqN% zFk9pqJ+;d<`BsEG6pS{G1n-Ju64A7mHp^0?DCB0(r6Nbe*48qUoAlCJghzyGk^(ic*ks-sB|z3MTTF zEt)c#n)2B#8ezCob_K(F3ppF^q%BoTF0)BvA0Cyak$oFWc8!f$xI6Qvvkdn0_37_F zVv3Kyp#hK5Z~O=!U&Dm(H(52nx`tlLJp^v>qZ#)i&!ej=vEy~m;c0GM?g0B6^$iOc z2ebScgEubc!G2UEuL!c@Y_$B#%ED2e;iBs*WmGni&+pRM8v^nzqYIN_irHC3%gkN-nJuXu2HYfb)d1FC8moij%@U8SxKP2>W{jH83;D@PyG zI7&gSXnOS26^ZzMO!M6GXi}BS1!c>f8f#61+_75KGw)dSUd&_VFYW9_<54fqI>V%- zAtn)J{VYta^hmBSgAIo-!k~{Dhd(g-%2WgNpp6)#Z%iJb5ADnt{XgUZ_4R5ogbcuf zxWR`(5A`I*=qHm0=tGw5QNPKNR_60--sDfk%yr0}W>2QEzA6K?>lZm?zK^ z3_NPg59oCU9yR90b>yGIpn)3sLq9O^s4)+qW~2Zfiu|D^8Fy;GVrL8D|7|}kDC5dRZ9wRwg!k1T8tsDR}FGRvnm6R8aYA*1CJUx zLc22Xs1YM{7z2+QF+!&@@Th~4KZ<1x@;HlQDIilH=5v<4&Jue0xHFAUwXW&sIM(8vK z9yMZwE@$9TBgW;(e-{-14JdMio@C%rBS&Zq1CJUxLem&{)QA!Kk%32z7@-!V78pV_ zV2~rUGy{(sIYQkSc+|)d+LD1sjToUl7Gs1YM{Hv^9v zF+xuv|5Mz6B1h;=1|D@9YdR@_hayMlCk7rhVuV_f0(dB5gqC4SW2j+}BeVtsj~Y2b zTQTsckt4Jx1CJUpLdP)hs1YM{76Xr3k6ck8PhiF%N7N-5c+|)f>cYUIZpPZ26u?6f zBh;ILM~xUs^(G^!0BAswBXkA>j~Y2bS2FOZks~yOfk%xPp{E&m)QA&$ONH`B116pI zF)4tDB1dQr1CJUxLaj&vJQOiP?HPE~h!I+ofv<`Dkt3R|8Fah!J&Z z1|BuygzDYs7eE7wJfSTac+|)d+Jk{djU1t)8Fd%h zpvV#0lYvK#9HCr^yqehI-{GZqaoQIutnD{ABjt<_Y^}%xr&uqehHS8&Uud zMU2pL3_NPYSPuEup#q=*MUK$+3_NP&2<^wfqehO<@eDj_#0Z_wz@tXAP{e74)1ozl z98o(k@Tid|)SZDxjU1tE7Wu2AF%{m#InMvhQhQUDJ{p3w3PJZi)Ut;@ipMvPDo zr8{)OzHIVjTmI8aYBk8Ffk%xPp#xNC{Lz3xj?f7V zJZj_!UC6+rMy^m}w)x2eN7k~W03M1sp|u(K+Q=U{qS=mtN8N|DFDZbBB1h;r1|BtH zgwA8&Q6om^dIlafV$`EJ$e@8biuF7xfQKSS=sgA=HS&bMVBk?BM(9rl9yMZw7SK@s z^h<3(kt4Js1CJUxLhCc|sF5SIBLj~bF+vA2@Td_ZG=Q0a;~xe&LKiXcsF5wyn5_+Y zP?ohEDS(F}Mra)d9yQ{GwrAi`_eK6F`Z35AHFAWGXW&sIN9cS89yMZwZeZY1BTncc z1|Ic!R{aGk02)x_2u)z%Q6op_O9mb_a)kb3;87z+XhBi{4@HbnCj)x50fQW&4H$UT z$PwC!fk%xTp@SHB)QAxp$iSmUjL^jl{9^U`kG2v`W45;BK{?j)qyQd@9HDg?c+`jy z>cPOHMvTz@3_NPY*dO`(Giabjj?e`RJZj_!-N?YBMxM~a3_NPY2#sdoQ6om^eL|04 zXh4x8^c4e-8aYCLGw`U9BeW1HfQKSRs51kP8Zkl}GUWBDL5^s8GVrL8BXlqWj~Y2b zCo=G;5hFB+fk%yKp@m~q9nE@?6u?6fBlH0Sj~X#TUo(0%V1Bcj&@cgb zC~|}rX5djHM`$Gm9yMZwHe%pWBSvUv2A*oYnj@M+803l?IYK8f@Tid^bO{5G8o5G^ z*%lxVDzH{01@KVBSP}Wxrvjh>MV`=(3_R)qtOH2_JQO)X0~mPJh!MJofk%xPp_>8n zM*{|VLceYKl>>7L&;U%D|&WjL>BaJZi)W zr8xC!rsUNhj*hD28u1VN?v; zG{UHbE2T6jDU)3@ITGfRvHPLdwcBAr2M#{fCfq2mcx!k@tP6Fd~pv+jiPTnvwc8wYh= zVlpv>J#;G3keEhHr^i;{^bJ#=Y5^aci#C-P91%wB&kie5Y zyzxojlj(c(!$k5kQ30{I3L%h*Gg6UcW3v64M>6@h$L6KmK**AmEACf2cst|$5t z8;FhUp_{O;$k%x3K@IgOYbz=8-9~ID;GsJR}L-> zKp@^wjP*foKo1ef_b?I09vV&{-y_6P_RwPl@;y$RU=NKTknc(26np4t0`Z<9&cef> zzzGcbo+A|Y&?o}=o+mC4QLNFV$oC>~iGYV*CXnwH0@1=>C9V;O_d0QdJyah}q4x;nn?T%W4}Cx&-bCUdduS4|4TJHBiezp;QwZdn zN~96+&~ze%cuZulhdv>Y?^7a^J@gq7N<1fCu!p`R5brDEHNrsSHSvZ(zFEXu_Rx0( z@_kQyU=PhEkncz06MJY5fqXv`U)V#x5{UO3@tys5dVKXINaXvI_{ARjn?Sz!8wl6P z@WyLp<}-mE=mx%;((SUQmJ1G*Vd)E0WCuyUwcvq_E1Lx`IaRu z#~xaqK)w}7E3$_=5y;n>6n_xH8~-pwyvD1`${HGfT=(F52l-Yfbzu*6C6KQhX$|(! zngsH7C#}UETAM(=bx7;7ht?wyZ++4R?4b?y^b7eK|2%C-weimw%kt{lR&(^h~CH_4Jy>Pr;%@8q91!`e**aqAO^CB4kD26 zU}6Y+s1JdBhZ4isLx&THcLXt#{Yd0LiVX4{O^hMnp}qw2^&`fzhmIqV?|8zWJ#+$r zd;^F;_Rxt0;+;fHW)Gc02;@JNK(=U1Bc>C`cLssm;h{4Lnmu$4fqd5zGJEJc z0`aaVHW<8KEjE%tzMF{61Uz&Lfqb_T+t@?56UcW5v6DS?7lC|t6Cv!OdkDn4m)OT1 zx*z!?-vdOb+A!!rs*&#@;xGXZ4I_|mIB|qM^eBORj}gb&Lr)NhH-bR4@F$5=1mZo7 zT+eU=dX_-Gk;FOnP=!FgQN(%n&!=cJ@h_-cpngn>`C<|56K|kB;pYP4^1YJ zZwish9-2lV-*npi>d*}xO`DPQy7XBmgi8xK<5TDsYzYvJ`EAdUwjc=si3FP~O_{kpni$K1=2@|cs zLp22Q)sjl=p*jNjnv$Bahnf?J*MiiNy&i=Xfqbn=^Rb8KCy=iVsV#e`9f5ockQQVQ zEkq#S!lXsmLyHoKw-{-0_Rtbq${+cbBvZ=JP_;DG$hQosJpm7OAds&kX<7Eras=`% zPg;RJv?76cok*S8Ln{%8x3ZSOvr2S5jmP??%)hkZ(;=clOX) z1oEv-T8BNfE`fOKk=BRTbJ2hl`8FhNM8HEE6Ues-A=pEk63Dk1(VRWB1%Z5960O)n zTN8-44bhf8RNsz%A>a0d2LTW5Kp@|aL?`x8PXhUNCjMa$?Lr{mu7nqRXg31!dK2B* zLwgVuFFpTL(Tf|<-URaPL-b`2?MEQr{=@+G(18T<9YhRf4;?~OCVYsY?4iR5#50Xmxd9C# zkna*=DSK!zfqa({%h^L$5Xg5Wv5Gx(HGz275Np{(WdiZ8L;mZz0o_0#-;Km3_R!4) z^4&sgWe?p(Am8o84))NU1oGWQ>}C%QArS8#0?~#b|GiY~BarWY;sASSD1m$r5{KAB z4-?2Yj0k5BJwhPgqr@@x(BlN+JwZgUhn_^1$oCX+8V%Mnq{#Oy5lO&9&k@L1A)?qr z&lAY^0ujv~dXYf9mx#;kp;rjRtG`OWu5kl;oj|@fh#2jMp&tpv z`-#Y55B*G>MgCuiuiSusBarWR;s<-^PXhV=B7U=nnn;FxHKbYs9x4%tS4V2f9%@D) zUUO2t1vj9U1oE{awPp{c7rWJb^OM@JhuRXz*N(IRdnmsD0QnXoEzBNTgh0GSNsAdg z{lAq|6eo~x3DT16p`{4qTbi^Cd#F8ud>u%UExaR9mO#GcNXxT_Rv-{>MN%jBP-ltq zN4}NFR8|`XtwJ^Otx8&rfQMEmkgp4=D|@ILfqZL_)?^QLClGHf(%S5ybqK^;m%jJF zq#ie*^$FzLfV3fdXd?poHYS>|hYAAuHYJ*|hc+jWZwsO&duS^H@wO(~5PCFFv?Y*l zJEA>%s0V?3I}jb&Lpu@3*OTbX9{LY~e7g``*+abu#M_PVW>2a&=}rdu_8@u^@X%fa z^6gC^TlhXiUjq5|Bl@$44j_>4Kw=Pk=wJfz4k3KlLx&Pok^e9%hI0ctf*?4g?ohZ&IuSxVCNkJVpAd-mDUpf%(a0p8 z5y&Xy!ux3t2H;EZ3yJsmT1Qw+MYnZ9t5(5??7}UkZ&i# zlRdOELHU~eLv&#e?MfhCFQOarM}rCzZvy#tCwj1l_9T#RFQPYlXdeRk_9gnUhxR9s z?*L*Td*~nn@eU@2upgqM{C&tE-=V}X0v; zI0EsGC;Zt%ClK|Ke*h844d_Gy`A#AxvxiP0kndDt8hhw;0{PA$us(R`Oak%FB4)FP z&LI%*TtYvO8_@X#@?Ah6Tlj^_Rt^#`I=FG347>L0{L1|KbSpq8G(3h zsK4Cc^=h$#4Du~VH>@Ouhpr-!Z&B*6W)EFMAm5VIU&|gU6Uf(|`s>(3*As}h9Q8M_ zhi*jv$k&N(*rYZLx|wR^TZQ^tNa3Md3FPZS{cY@_+X>`bllnW@Lw6F0w+{7pv4`#^ z5N`u|e3KAvK=%;Hw+Z$4vWM;?kZ*J9?`ID^Kp@}N)DLA3JxCzm_S8Sb9(tHSyq&0z zXtjrRtK#zoXm41^1L|oUa=(!-Op*#|44=(f5oLFD>$XtMs*T%3a8XnQ`n+`($3gGoe41^Qr@d~2uH zEWb|uUt960I|Y4otBkcHZ^8Fz8u|S_ZAHnw1N!BSJLoPu;uXypNLg zzxTN+dyo8vekJ2r)cyBzbJ*{!Ph)e_@Cm=8ct3@H?wsIlOS)m4Lw3BH>=SZwwq^)j zCs}YOi0@?F&^#+qmoVP5Jl?*dALDW!>__3P4*D_fkUQSkp&#RNz1dI2n>)sz6=(8% zKcIH!DLHDFaXP7A51159nKsikwu^XHOLJDUB1$J`dF$+?RVQg~*4FR`8vks8;gEUt z=I`ANij^B5)x>MF<7B&I#z*+`6?;{aXrTBj_D+_TGGgylQLXsz=dxf7-v-1X#b~ zd0=g)H7r}G&fm*crJGW+HDgLe^RmW)&U0V&h*rOmapxyZSF5CghS}8AO#S9e)uYNzF6`9=w?om9R_o^8!@_AqQ~rawQv`mL(k zjpOBx-r6B{dB0;;{RUC`p4nDbE&+zEH<`a8d~vZN36J;v{oS(Jt_&lhWlYjY0DU4pb;?l}hyo5~w(Qukuy=l{L32X^ZG+E`Ie4AOdNg5-)zv=y{D zKV*+3+8TC4@ia<5hJ`A%lb0;fmeB>As2V6AT%wJWibiSkoav;e^xd`vGNiuv%F^Oe z-%zdGELhjq@U)SG7uV^hF?DNLS;VdlF)wQU%ZskS>BqjiBo`>EE29nU9kFbMR@iBs zK19TA*V<^MIu#_jS*BF6#Zf*z87D-Z6{_nno^96fz8jaz^BC%B5!v`&_)j(=F%bho zw85_0e&^+P_q0xuZ#IRQpH3{e!;FvrxxQ7T{Dx`3$ zls<-qq6*6A_gU~a-WIrRx?VoqR4S&m@rk(FR0^?@o&J$*e+( zIxGk8l+5J09i@?)wGnSSN_4HDF4)LZswpSX(y(?B!Jg7gx=V!ZKUjkLHkSKDjQaCh zj_e|pkY{v}1{7Gk9zVIa2wG(qshCDnOE&kC`jglWqw9rfUbJqkRzBw?Sx`3?-A7(h zK}%kxs3m{)lIm(?*|nSGK%cSp>_&HYmk%4n)?0Oc-ctA6d(@C)4PDpnbYqQ(W!)t| zOVz?QCt_rWo|2w+Y->+xfyTU+JAR_&GG0iFLdR-ib}U zr3SgT4OM@#XtS{^(kWW+@7_|e-1X(}CY8utSbYrlSG4f5;k4g<`bbvwjSphj&KJV4 zOtg^I2R$V=ves5_h|ay;UEbP9@+@FnJa;kqciy&f@7?A8eI-vhaEN3_gQ@hD+LSO3 zPz_$OaZlW3hkkihoV%E;N87lx;c;IHcR4lh((1;pe<5l_i2gLJl(4#|j|NEN<;z`Y z?$E%iKdLj32r)?7N*~VWU3Hh2`Or7}{u(Sg4&2S4x*4{{V=Jn?O!*`)%(y2QfLLa+mjoN>^fZXH0K!QnQsr3S6|Eslhkk&+eF>Rfp}lIBXtK&iN;aYn|- z`cYD>Jk&?B%iSNt9zUV^=Qo-*u)e%?w0dUv4d*Vqj!~t*L6$*k$O?&k$cN5)AIC^l zWSbF^nG{XAz8yoerHQZPD6gf5kXw(XUo`pb=sd8@SKass-#q6Vo?b3~G+<8SY1u5p zM57ZxF*(>zDo%T{$B!OS-JxP~mZ3#EN)rz2-ZFzWVG~U~`G=3X-)+W9ZRk|DY^=Hq zZw=x(PU`>H?!KnHm2py=Jf}X}@idBbvK&umXBvy~I0MSDB=VsR^eDCbrC!uu=1&ix zjx}wLzdHP%{Uz68#-qzHJJJPvx$g+6MDC4-<038h#-(&)?$L2m4dM}vV(e(zKg^2U z+YAR=BHc!*(D6J%va|G2LrtI_MN|xPij(LNLCVEIK0=N_%oC(zO2GAU2zAUgf>c9PKvrcQ36#+|Jp?nsDbh*AJ4PxJ zQF^L0oBq!xHPLIi=ZRnP@1As=U7RLW(Z~^Rr%AFVcU=FR;N%(Vti3WL&*{%N6t!gi zY#MbNeV%}Gr;U6Vw>8OoTkfopQ*oOw-IjYYH%=PEx}$}UyiA`b=s)vD%%Qv9(*JAT z_@VbJ??V}f(l7#YwW;bsr7qWLrnF5C4E$H{wzDXBGdd9Iz<4{0cD25`0^>;)=M;z8 zG!XS=&)F14AWd6&#%ww}1b3kPLq|~l>P$E`TPi6h(5(=ClTWZ> z7|(ZinfAp&9*RY3$h2(^^85e_xKjX~1`f@UmeZ4$m`jsP4K!yyU3qPpE4|a0|5Crx zK2}~cUus7Odg^?dkA^fO3NMg$;3+m@p&HvWgQzjNEK*NHYApQ>wHib4BFT}?$%hxI zr!{qWvkW(@!)3o%iYu0Th^WPD;~bK2E~ZImA0$P|m;Gr-vVv$;^<~>7bguE9fH7KP z9HS*TMh?NAzzr#=GEvzqrTf&7U6;~{7(*fZF3mHm-^|AY1kk^bEOgy=ax%hQtox$-&<{%6t6@~#In`WRxJ&$t4za-HOuqYRgaY?Ef1%0)N->mISYC0jgJ>K=M5 zEG8%Hqh*`yl}_rY{W(;cn7hTfkHh0^IAWe-iw{b%n%q;>FgbP~P5Bvz>5mApjzs}rGgTznC{^~Quo;Z)%s(P(UmVP?san?}x@W_$=*aZP&Q-)E)! z>Z+wgx?p}5Nl#ue5zgl%MI&dOrvsws6*@GwoT17lihh~xp!;{YLMO#r=%rkujZK+P z2cuV%)CK!-fyU=tlvIi8XHn9E+#&vJn}qRHM+0`Aj+q!)IWH|UtiaW7teH-2D>GW_7dA?W1jKbcM!l?RnZnYK2&CO0IuKB|^kXw(4ArLlP0Gank4f(yiX~XD_|7hd+BQJ@6I?{fv?w(xg3B z_IT@#ewzH2} zMQc+t!mX&TwN~rvCQmA^>nG1BOs}`v*T=@?zI71NULCnnW-kBUNEh~<_@Rs=*RSTk zmz&Q%q!w?Ix?%KQU+sS{x0d~hy7chV)j47H=P~35MReX;``Wz0muq2zhg$tvx%|91 zE#Oj*7f7gUSm4iE-5&0`Xob{TTDy!v5TNUGB?A)_IzE_olHtzlY9Wn;9T~@1f&2 zr|89_b+Sz_ZE0@5y%ElXbi*{70rIlJx?&nHdCy>7f9jeKF?9WW)b0Ww zwfoG+(DfXqE2j5);ad0YADPV;=kx&&uV%MXqF#xnZqe^% zmB<#JhULW;zt)Q~N4^v`wYFWs}FGbIHe9Skz;0I0o@2Ji;`9hn!sKA;A_XM`bm+fnLta2}<07mPA=-;P$h z=3~_EurY>itgqUA=Bsww_!+u~$Ew{cW7V$nI74^Yc(uD{yxKMQH+20bsNDq<)b6th zhOTFz+8q|CcCQ2)x~`MdZktJJch4k4*LI59b)KSj7fdm9-%eG#=F`;fuxW;F>~yvJ zY`WTQGsDn5tXI2N^lI06rlGrRmfGDjOYNG^Hgx^w=!#V~9v0L2a9D0QJQP0tnFu}{ zj?0H>@pjt{M}uNK3XHSAt|2TG)< z`pds@vvJFflQWx-^E$HYygUP8+=kq7GMq*f&Rae9l5)p&zw^R(L&CDfo}u!&CuO7O@np5Ql_n``kqfICwwv^&r@0__P+b+ zD&P~Y0xYfmv?AzAtEon^KcxHX{TQwH$q2iZx?oqy<{)2V*{9NW8{ge=N&C+|hQ5q& zyr`phcXI4Z|GlgIH)sbMKBsvG=r=l_1c4*%q3WjT3^#c(rsT9DrG z>*ZwXQhav{zB6AYVx8LOy&2Fr81Hm6s zE(cdOZI5>%A67PPuIq0()6cLG%Uc`P6V}$$FfYn?FwJw#F2zw_ee;l}l54)WV{dxVt>HH z&^KO-K5zc-W!lYF_P4O|FpOz6GjZK$Vlsr1oAmgJfy=R*K znPQ`6{pXH5gVa0HcQf_(Gkr@ReL3=%G1QG(u zZ~EtQHcY$fQ$+M_(_iNL@V7Ol+P2FUJv&ye^sDqk@w2;+XYkc8BJ`|OJ$+xY7-iO_ z*|>M#L?x-+tNM#m#o>Hc3N?T8MO@lC*m`EohazcGzA=yYd=u@a=_ZxGk}TFb>)#(M z{7p>r^Se2;|3eX}on3U)(kJ5Z;KWsBM#PCtry9DhE%i)vvdMRQTiYnT$Qs+eO23Cs z#O(cM4Xr-U7U9~B``#~jAx>GJbf3QGy6Evk)GF5Yr3n4ftYPq(Bf>52WVPcVZ$#(I z<#)QpUlMDVyH(A6^hSJ~ynD#>Hfdr{>06I}`Q(VQeJeHp>YgSv6(f&5+xktc+H<(k z%m=CBbHv^aZ8P499(O|PMOy1q#k(3^bnl!#i^|&?PCa`xMa-`^`ghII??vG?pPf8< zCyA9emW|2!@LnY4FL$Z&+54i}wm#>i>z~E*GoJNMRKG9Qr-#)nn($S;J)!+nHSxai z`cX|dIb@4(X}`K`EqYtD7*!`q8}eCLOrE{|^y+xAc5Qg-^LHP_>*hVnG}E8GD9#K% z8$9^dTXE{ZJ+0}Z^CC05eAAmtvP5B~ZN=TezQ5LN9OX&@1V^*id}0ee0wR;?~*g2b1Ey ziGyx2S3B9Ii3-#EiR`7HMbqvJ-{~XV(!}!SvvlL?z7x^i{8lYpmnLqOh`!ji$~Q45 zy6l-F@hM`!!I>$|m*$A^j`QqGoKF>7!rV$Wn*3FazPxpD;h9NdVys8xgj304>vY$L zU3;eq>pNFkT|NIyv~1dc>e50tweq(KxlijQD+V;^l<}5wZ0%M73}J`tCnO zni$!#i{Gq9uSAVs=I)oSCJKGQ_)&{jKNWX(JZW6ERhszl@t<=UTe5_0NK8}bF6YF- zhP$es@P8>PI{4?T?l@bl>o=(Gk{(Y);bK1H*LK^kw6w3Eu+ihSDC$-&@~M5OaGiS6!dK^JGlAfE7>FNoQdj=!{8s-KS16X7R$WWz|_+{jBXq z%-Y6{cGr!xP8H`}wd$;Q9`$GtX8no#S1v6)pDg0e$Dfc)(nP<4^37LYAB*W*uQ{gI ze<;p3I52;~stj?~a{WYU!5wjVi6*nI|1+WQ?l;>ey-1=ccxFwB&1;{FMJeYCRq1)Bgjtb$wfDu|5(&dsewkVzL)>wzW!~t+Eun35^Kgd}>B4;N5R>bzFAKYx zEeq$n`%s7SXg9SjMtboVr}G7-;jC{O2FZI^M2mBDE5@N^MFpcpi+#&=pQhbY%G~s*;WZ*b>7N1|Khjuz_@o{AgR zb>38QP80DNV^;h3ju*GCHt6NFGf})8k?32#{WEd0R>|*950gc{d-)d3Y5Ype@7VU^ z)guo@=LM&ND>X`0dW~OeG5^;iQMzx*9!2T}DUbHtbof0kOMzh{V;)0-dIq^F7O zqn0*@szeKuij6uqD|cEcbh5+m`i>dm=*lVi4)}&BwT=bOyrO$2nv}PkT3qZGbz|mk zYf$uw(0==AH}}&;#p%pN*PaF5ikt@hhqTQfp{#4~nxu~qZ$+O+jyWTm-%>{2s5#3s z?7b-XE4W(cZQGTUw4H9TnQz2a`ToQQb+0Imx;AhMT$d@D?y`Sr_cTta)FkC?`ms0S z`K`MvS~tF>3>fhw@MjVoR*!D(vCDr?8I`>?bxP%@qTY`j-}1?c%EaAOlbtGk6z;V; z+BOtX;>ht&`uK@0zKBMyjWQP8yd@rY{yJ{##IK@g^~|=b5)(w9#IK%($9xt+LGyxV zY0r!FIyNgW*7+h_`q?yD9~38cJw0+}>(m@^G~TLt!9LNV`{`0o0y;kzQrPR=@k>+0 zo-^*rug7PJ`Kfgh%9KqIy}VylOFs2h3<}vYsKbQoV*MSx^N&^i--&wiu#92Ocf}H` z3(IV(zZdR_Ha8E;m&Mgl+clL6e-OTnMsM%)@vK;A5tuc7-WSoNV(8|U<&(sWd!RyX)6G{wuay?CKZt-U1b>)LN(BNZ<(8y#_?M1uVN6Wnw z2NwVA86T7;#_RKIc75IYR8)MPn15)KRN>d6Rg*?f!MOQ&JAQUcN3Kj#@Hga|S*8wfOk1-iaj9s`$NU zC7sg6KiQ=+I+sot2MeSQaO;{bj&8j8TZ;OFzzYVF}`@9%kqsr!R?`RQbVYbNf z`B@Q}9jEVD`;iE$Rkh+^DMECcR50W0g1h49<;g<|9NHshG`nl}Za|u-QvGsC|KDAN z&C+~35xGa0KHpG1W!`e-FLjbs-yNz>lB4|=tCK8j-|;P&WKK07*TSSJTiB!*Ce3}f z*G(|#a`r!0CtcahH-$0j!bX)+CtaIo%hgLTpM-DfC74Iat?DJ4ce(lMC0pJ5wWi>b z@8hDAmGrpgD>b~PdJXr&%S*k6E9hB6y#{P&^SuhL0S{*8sMmny4jg}hYrv}OW7RWX zXV)LuIPP8Uhm#-9oPBN#{Dw1Ue9Mz5*NwhiMtEXO*1+&a?>YcW!wR-A3eDg=$A=CFL!#ZYF*tz$%$k}&iY=w^@qTmD9ptgOJ z#pW4nN?A>h7fY91o}c9eu1Vzhf;!LR?&6_P`piJfDU#kv{xLvJj3B>E+8|6*70 zf#@B5t>&$Tbirbt(K-J6d9l7&_~hH0?ugVI$EP>mepmFkaml9Mky~QqhQ9up^%BK8 zeW=IG*|n|;CrhtQu9XtR<)2L~GSA)?6Z_4w9cY>&T8QDDlgl$LMOgokvg zV8WqzakcN#CIP=9MLX}=Ki<$q=HYK1rrL&=#F@KG>({GvPi(2PCVkNkMSSfy>dm;$ z55zpjZvtEUn;H$)p(_SKk#|ht*ygGwQk+ zs5!fQ>F7&Ja+me%UR8?}4cd>M*ks>*(Y5a0_YZZEB4TT3&6@XLi~1g6*0;Ash>rzI zeDRXDC_6T9aJ_qMyV$nFed);!Ny76V|9oS;=ZZ3sQsv#JH;9P*Hf1MP4^s+!eB!=1 z^r2oU8uC?GjJ_k@SWoEJ;OluIj#MuH;cSBNE%P+BipO(tdhzK=Y55VwJomk= zoLv~d!nS{$m>pTuqh+-iacgAP_71&ni@s;)KFzoOrgG%+kW0_wE{m?c-m533UGGo0 z;>6^U{;?NMWS<6BQBP!Y;!Zjf>eEG`CBxKHVam?c>X~ioaBKC<7N2#y56+A?%6q71 z#|SXH2yXPPMC)Syg<3+YPw4f1L_lcu+waQ8C}BC(UtbP+En1W~ zb0PHUZDmlkvM~i0T@gdS#d>76j8#&PZYyq5I9+^r)vM9|>-Uu8HkEr9+jd>_`ttSe zkHQJc8K>;G`h6LxqGz4y!Q-7$lqKz>94-&MCH8H4dam)WbY;wyN&0c+kBbWjxA-)6 z%TP46I~IFZCrLaBag1NnDov5kHmSbD_PSVcI3m`o@?+&yDc!=7Cm)CvYturqil!-N z%0FuQ&w~%*=G=(+uZtcLYcDx(yxBcRB)WHgH`r#Mu<2cz{%sF9T(-Y zg8Y=^55g?uyK9TN;mXJJ7Wp65{v;;;Ozgh<(@~||;!-!wF1;54+l!bL`Vymj@H|%} z%W!+Mo*u{|h|MA6mp$s1 zA_gA!&3dkXM_181yn`p-6;7w`SIB7kLewj{@OF*W$>NRW3};+J)|xn1y@pJTYISim zU2`71WvgCu>a7ZSs8_A655`#Gs`cvS%y3+U2F&(UFG7>H%+)2HPZTjfrk9VgPrN`BRJD#}S7Y^mTdV0!HqPQ3w)qH1~cp6aGzCrm^(YVx# zLltJHi!1e?1WgD!uSA}7@w#3yNiW{zZ!kOP!FkcN@vvmkJzX5zw!rSvk88@3J!J!0 z`X>v=B_*qW8240p?%eq9t~z%_^5AusyVJtT5d>AZzE#XUe)xZ{N6%(LWG{ zZ+EdxjJ%+1`dFyZ#Foj*dgm4|3crt2vNN>O;1N+u@|ay2?z?U(gC2KF>(=d>81G#& zxmf9YN?FJH=L60rD)r|!>bUjD1Ep^ENJpQvIW#149zrEJ<(?3d67DPN^i}JL5tjr2ZYO$&aonq7P&UWqpSec$SucXtUqvFXX^U&*EGZbm=;1({?X`=7^ zr$5T&+*Y2ibF$Cl`6kl?Q^g_B^o?BvBafAin6lbAjcRdx;QOs*V1&t z4rQEwsmzQ;@nVf!)7K+upfWY4%z_z3qeR-+H!bfMI3^mkcv{r@cf6>*&8dR_+|x>@ z<@u64yPg-{svO?Yeb9cT=>Ba7!h_C(P#NlcPmwY@bPk9;GO0j(#882c!zCnK9)jyF@9S zKI@aeo19J+2c_LfnT=vZ;M`NEuIA96%j3t~w6;C1)Qhbf7S}LMyg1|KaN}m2h;Jyr zejlAI5Up4K?DHQu4+J{ftH=G`uAS85et7?l>Tz#h zc$RwHcWHKAJ&zRBmoNAZCz72#?yDz`$Z~r1#1YdbMm_OV8`$RrPCU(!M^A5YhHo~|p0r)#IF%}(O!`P9YotMK%Eap+JyzboD2jy1v4`v}ut zGXl3fQ(E*)J-BN?oLIbhRk^a=-|3YvZI|5`Qtz|~YZQ>Z_v(A4{lz9Zx+!~=k9|Kn zpG|zHOds57+}F_;gvEy+AthZtD36{WoZ{DZs_1)K`#Q492gUq-seZx<`La znY(#%*URFlEN;12tixo?5-za^%`VPge z=EY~R!|sXLZZi^ogiIEficc@Nzd5~1mfoUe`pivYZsdn2*G8p@YbK8>>!yW?$Ss@G zEH9;qj@@nHX ztvAMqo?_nKp$~3}%-#ch2T2K{ba2a;*_Ez~eCy7w$@)Mis(YK)%@KD*(M8KU)~S3# zEbFs!_|qz8H7mZt_UoNzups zcWCD_DdOVHiN2@no)L{4zB-4~HQdP9Q#-@Ar7I0b9_@VT%Oxd$efLwhJ3UamJuD6l zK5$X#a;wd?%BRmN`vRWZAAS&}#Jm__)xFDY#p=a&v&T!WD{eD;%^2QpzhYB%L-DBZ zHx#Qq`n2EI-tQGb$6N}Qy?95NIW{9^!kAaexml&c5@kiHowTOfe&wkWlh5;Tmy4^E z^sEXCwy%7koI6*#;#-O)=A6s&N2?wyrp-71<5MwQNneuI>1ga7W%WO$?{um;RSbNU zI^s;P%gRzm>mN<`gewbPEUf7$bP_r-6Nu2=UK37vaR^twK1 zh-djH%CiXXp`UZ&L`CzD_NIMbDgO^i=l#g_*M@Pj5-Nm9AuGNurHx~ij7TXmN=OPJ zl2N@;M9N4;WF)e)_c&I_-h1!8iH4r{InO`f2R@&3U-xyruG{cOirn+`>A?6EOZFby zVR%N~viiWzA7H^H@2q%sFQmP|elkm}6qrdq#?w${Ab9{y!zY@Kc=|o+NXFBzX@+V6 zO+UXQ*B6eF4;XRsR23elCKgk8oKARu!{hUw@SGGHpXnB2c>K1g8n&SE%ePT&E4OPN zzJHPxeX^FEVk-}2fAr449U2pb{3~r>cWalGT+kS_R+CH#Eb0Zf)~{{`?VpFy3OBxp zu(g7e)4^%~l}y0)vNrj~+HL{}g}&uI*)#!vgy^syN$dhDiNv;RB17;)(}I75Vk@Z7 zm*XCZ8-(4WmYV-rcLVl0Bjr;%-Ec=FPrCfh0q||_6+tJeG59*6%GN=t3$%*poxdwM z1fSoSHuYHS1NOap)9S}ZVAO7JdP~p;j(?E&bx^Dc%Iq2Y8^uCYLi^*qxs~R{uu9I~ z{&aOSJh`d0!6TLh4%oG9s~_ut{5Ic=p1GvLlIB8-V=8S>+cHi!q#z&kw@yoX2KB<= z?qU{B)-rIqt>nJo(IF^5&7l}^xD{Bg3JI5K4MNSx^0~&G5}^Ok>@3U8VJQDg{g38a zD)?P;Qz)M}HUi&pY?G62C;&A5UZGF*hoG*@gTJ{iQbAw5<_7h%VW``&_|fiC3fLC< zuP&^95H`gwx$D+9gCBXB#@l~)!3;4WQ>UAwAk2I1zCdm_+`fC|K+?r|a6{l;inB&L zym_Ymh1L5J(8bN8we!{xIrVI{e>u#6FaIFaoTaDBbvCd!fDwJNKmrgJ7~dq}OP(5B_j3*I7BX49xUwiIL`$ zO|YHrX3V9^RnQvv-a(YE2=Ub+7f6svi)u%SPde|&DL|TF9V@hxd(hH zW59ctHmZ}gOF(w;?M9T$2Y5p?^4zzX6#(y*yta+40#?2=zPo4U0bwxG)797x3gb&k zMV&^#$=U_wDdm39c|oIdOJfG~j5do^2R8$jchQqBhC`r;*cLe6Hr@?PN;e8mYIFm& z&{uKaQaV7#UG=J0hf3l89q+n!lisi)9mTZZ=|+$kCb?5}YY40xSr=++4g=SqA{Q67 zA@Ht^Md$9>Mljzrb7)n27_^-I;taBe!8Tdwq|Q489*pWd1Pr6#D2@A_vtP-;Vz7(` zEvLl82da2Gect3^`G;Ia&;Hkmm(dh)^%}H{t{oj?N6ToSk?jQ9Zeu6>i_vzganl5E zvk`A<@ix0F*K-MN!yK}#Txc7X;;xdsoHzuv#=`gy{nrGYjORRWyzGV(4JL(gN|9jP z%~$cHcr5{ikN!0zNH1sPfNkmY>p>{Y2ygWM$N}*uv-ch3YJom)GE%73TR_?PYvmVW z?NIIIZ8iDpoj`e^x`yV^G&~VL(-CXi0e-U$tW6Xw!Yuk9d1qp3!TqG#k9vJe@Xe2p z=>bLQ;Ine^vHMJmuzhtbLq56@JYkNx%Cb%@!q4*=1%XdWz!UX{oi=0h@W=@o2Cdl& zU?56&yna0&7L*N(&<-rZ4Hk~qYt_ZD-thfnT4DzNUNtcb{hSZ~JI1g1g>D3Xvj1pC znb!hO_p!(=ew&8%0vU5pyPDyt5^jN9))BZ_K$yw7LCK&N2}1Lve)>f|7~(nVa*s1M~liI zx5g`IQK{-=`+y#%<-r~G=wV{vzbcC!viQ0r{E#`A{Jo4`!Z0^k{1WP|pT!T`L|89= z*yJJ^h#CeFwF(!dxR%P*54ztsD!y z-a9M0vbVvJQ6+)FGvC2A&y7#XlpQd-aKgSP?;DU!xf185(g_XeTF+k2O#zWKjiXfM z-B6^}yY*LSGC0yCS;2Rt9||%OOKPD7X<&DogH@hNCg|AVeuKi#7yMH#zI>R|0gNBl zQeAFL04Ih@Rn+&T!Sc3?r!PCD0Q4)ZDG z`d#)-o3RqAwO^d)pG*f`Mq5{3&~$<4f~!T#&jWyh*g&ukXa|l&qI_VdyB&CwP@laP zPReGZ;P?RFG@#Vy&TC*ovXYEir!r48etHu$Kj5d=mGV&;dV04S4=bXlH||pr z&WIAztL0HffSlluETe;=&;l>qZI=nQY%sHlvVg zO^|97g;c6g>@f52y#Owt;AIig_FI@97A3O2`- zlHQ_Va~sFaQ4~~OrlYXUCCH>Q&UW8llypkn6*ECeXHA*Jd6abCQIdFplFn@YQ5mr>M{rnCALC@zLA@43jl1wP%MAUf=%oO2raXl)3s> z`iv2jHm$wMrQZg^UHCq^b1y=VPt6SC3*Ujnc@84v>EStO__n04+x8>Wb2U^ss6GZc zqOPWk(wBiaH;daLjZvR|uumlscH)W3- zwS#HqYR1<=U2uLxG-#Et2q*zcO4HM2aJ9<%duwC}wDRTprA-(rH^dNPevjsn2IHRm0iO zt!A1!y72p6|g5qzBwXe06h1Df7aYT!^}xB1KYe_ z5Z0W(jY{q#^iBABMpKNm?GEJcdnMHckFWf7pSNiOl*vDS9osB}gyj+T^Ow6Cz$fRJ z4~}oYfN!RUe_g-T2!6hm<1nMEf<|`fA$lFn;NRT-z-LKTV2AbdRIewbc_enxwOP{< z^qDtlHx#u1PInfHp1=wyaZYdR_*y;4uK05&;fX8!`0CV=cB&50d(O~AyfmiHyMU{f`WyjgZK;-?8Xx&LO?l?SbmRGktRK6FE+rZ%Zlj1{mu zBL8=*bsfw+`;DP(vmORX+|4*1-3oQjls;Vpl?(YRh^I(%v*5ywObuc*k@IL+hd5}r?o2(+&1iUc+JzkakP#VaTeCWS52tDtV z?4)8Tg-?F)aqUVQf#;Ii4LoMP!wHIWtDC2nU>&14tu#o7(OPHro{OD@X;7G+B+Tg=seDaiI3;KCYGn*;SX#-zmn^(sdV+j zf0A?Lw<0}l`@Is#9_NZJ7oQz(X_4hpPgRF4nxD1L9wUpUf>W449?#zCaP09gpZkt2 zo7FH`HDuXvyBH(S2i{4eu1A)SU)@Cldp_*%(^iq^!+LcKOE8y74rV05yd46uX7g=a zPDh&U!Wp@0r1>u1OYTIPFM!8@)NqT?bkMm7NjTMkGOPi^G-7j+2DII}k2PRl%%=pT z0dL1#pfq$Ch8xdSZ+pCJ2C5lEpRS+a1mvLJU9>-<9Z(oLh+aD~4$B3P2mBf$qgNZp z7vFYILi)7W>Q%Ew&>O-oGd(g5FP&Dppuf-xbd9ykV;{`IdpTE9`&_C)qV-1^DUo+52HFS|gNzGS8;cONuYl%Txn)&kBL+dfPPX@oJCTvoQy%HYob zWbX=S=Rlgi9#7e+X2@1HWWsp00JPqXvb^@GUu`Qg~Mn0bdJ?Zt~a<0>P|0d!F~h(C>bEcxT@@uo7dFkCz*V5$9Ms++3T$ zpMpDt_)c2wG!qbwng7kN&exc>Fh0qw?}1*ctix$>@bPI74ef!@0Ty ziqs`vRNiQZjI%P5zyB?O<#-p$%+*Fnb#MoV^1THRYkb<$k9h#LYUhlIw=aN8I~0?= zh}Zq_?sl>5gGMvp!{R9olP9C_6ZgYEf}aFZw|8{L=g) zbsIQO@b3Mpu>@Xjeb-~Q4g_1$cI@2alYqDVpE!3^F*w=8zsrN<`}C-091lLthw?rz z>kK%iz*~;7CQHU%z+YnPl%}}|o(@^Eibyp8Z5`U@&Vft7hQfh3AOqZh7WYhe^u{E( z(A>MIFD$+m8b}No8G)>2|Q@czFQX@1{(=KFEI0 zmzJnOcD+YO9=7X$YE3yJzyCr|&{5za0>Yf-y31oUp$CfBZKgI4OJ?Q&hi;BVTWwqtSR@+wAU8+B_K zs7$2sA=B$BQN_rDT(48EH5oUxVL^fGgqPQKh+1hsc#2}k(o~a+(X0bjV7fIU; z+=>sCL$;gGn`&&k{TySpMz-6F1?E&_yL~S`^Dyu22$YTPs2F|H3b!YyM!YH+ew-2_mj?Tgmxpx)E2A$!}AZkbb z9Mwpz8kQP@hYEL&cJU3v zh8vd;IApZJ#_^bf{CA5`c#ZjU*wP?;kgmdG>@^KDZZ9XLejR}E##@H@W(!a=A<8r` zh8TwbzQmrldS4DFxc4^h>THGd8=bz!9G~G)<2FA|<3cz|M?-u4R4HInN!)cjiu{3H zdmB#Vroi|9Jc_`EjN54J|0xlPfc>!pTiDBfIB6JCrC#+8=s5a~v4aI3Iue$X1VRlP4<)tlRw<1P~ zS1AAYTI@4I`G0Z&{ZW+nQ|=AKc|Tq7JDm4>!d0C2PurIrK>R>qd@tq)gpOQgN4&sR zv^3@gc5N3}LcGDX`{CY*H(*YcAn;o(xUj$7e{ld_^J>9QuR?M&etH`-tMSu&d~YUx zdiSgNsiL>srTGVb%O%xDZP3$gv8aTf?oqe42K4qP&p*O%zxnTaldK2vAlYwSb5Ce3 zl=XY>yU-s2a{ua_+euXcjn)aKFPjEgfJ6M#ceCR~P|lR`y-;F2P>)Q#K2X*K_r+*8 zvN=J(wS0}KSGEo|Z7P_&U9SWl{$t#oKU!fDCs*j|-y|U1wAr{|SPVwE4=HvwJO>{X za_UnSD!>!kx~VfYzQ8)}!mDDLY7iH+SGG#Q4Jgoms4!7Y0WQQD#4lI^EVE!1#V98Z~aBV}T=1Ouu;Cst{ zd7q>ksgkw##<=za1HlwyMR`w9RTx!yN2&{)yH@)8j+`@S?c&iLmh1u%Vq*f=COv^W zldy&4-zGo=RxlpWu}=YJf{HiIFOh~~7!UKa{8+I2OyviOtr2j#w#NO6cO>}hR(|@y ztr76+Jz=RS83k+=D@4PNj{xQN?Y*4Zq2TZ`(`DnhVKC2gn{VUUd+>(MAlo-@5bPGA zToNJ|mlW2WcyY0~ANCh5DqZpgcgaPC0NroZY0;)~V!{(|D!aWx@#a$%VuCjxVDR5b zwCOC5EHt4_=TRfOA=;E?#~tyelsYmR?%Ur2duYS0YyJ*^tBK+#6`~+~)%*Aa4Rs&j zu#LT1e z30^<2O~35j1)flx&PCI3N6c3|4Y%Z5@$^f6d=XE-Ta5#F`pKQ(bVk$9+8`NE)6$d(O*B2(ONH_HO^@-ca3;v{E6AXaDVVJN_b~zMuLA z$#(hYP@h0-*U;BZU&MyRtVo3+Htc&bO%PfvMw#~5qQ!!Fz5?$N8Ns{P&>q1WE@X=K zh@1Sr-_RcM&ypW6CQ6BY#4)s)tep*2M2pA;Y2Mvv5$SAST0xsk`YlNYw8{Kp438Gl zSb|}}w$k0@B_uI*cCq-^9L%$!;gG-I2s=L}%5E}Dz*ja6`^;Xo!IG&nKEY4tVFKU? zn5e3O?_S9*$8%0Xs|q_Ay{#sAFFjtN{_q4m=S5H}9#pS^A{NH{JA{YGCw2cplAIpK#U&8X0;+tUQN{X4!TrB+Oy8P<8+dby?tajVMc8~-u#MNd7zCW{4Dx4Rg5w`Yr^F*FKoe(Cmb1|k zJoxnFTP9|*%*|Wp?%25mLmx@<`ZTUu-NVnM)B zRMr02sS)_L!tBl73!OkFi&1dSvlZGIs18|JHiGjCf6r&ue}LJkgO61&6ay|HnXY;H zI`Gv^^rUW23HWw5ea7-wJS_OqoqJX$l_Z`%JG0wV0_q~#?5|lb33zmByL_Z{6&Tv) zWiWBo9mx0`7dL+24m@I90c&p=EH(LdHRpUk*gWQAy>X!hUV6HFXq9RZ$Ov`C=sw7Y zWhvJC`KJ584dIEV2fUT=NlU(d63ZZH5KRu@PE3TsLqhvYNc=93SsSljVU5vv^~xLN zBsVRh2bw0-;#F%Odo*6PGG&f$L#r02Rx;k*eB})A?zX1!D;KS57S1OhpjA!$=L>1H z>%|JV;$2UieOu96>mGR4BrHJZcq7nO@|BJuKa6zO*rfS_s{wCSKF=&mBTUKeNm7m4u7ZEgU2nPj~D-=#pK&)rW<_O^p_Y%CUYlvY4GSaTrkWiQbF&pc^#`Yk-% z^eBnTy%X?Qex5rR^$Do$zqEgx1QO1;$ORKC{7C?m+BI6Mk~`g@AWw}(@~uDd&?%6t z<gp!ME@`&*|WuwCw5?OWd@Fuz}~dFj;%NK0q1 zaVm@h6X&aJ0~d$EW!XK~rTM;sh}WJhLFDWhiQOkWQ&)R`pc{Fhg|VbXNat^;N7qyg1N|H<8^cE+^$vffNS$`L?R$fk$oeEqnLbUiY|sR6d|AHfR6GL@wWWkv zgjK>~eFyG47sesTc#?hCs1X`InK9E)n}il)ss6$lCIyR*Lbh{| zV^FE{1Va;ac~`*j@+2T9Q{kQxvr*;lbS_?=-s#HP?t^{o5CPwW)fUI0% zz`7*?dAGi=1j>aNB zed&6$XmJ23ba+t+n-&s8i^A__mjbkCP=V|Sv}iaL!>FR8(O~U8Dcn z2Ul$Nh2AKx0$$3ler4{1(EF|dn?3nJP3=}|;t&~uR<{TD_`DrYK}# zr$E#IMVG1aeL2%`-cP&t_WkeRQwU=Oazg3$?8Hv!W};U*az5Duq_Oj9$@u0Kaz5K0 ze8A3U=#CU@rS1_}JcpcAzgi3IoUTrM!_Mi)8&=qQb-!}n16i+XosTg4-CQh6xDX`! z{f0hmCt|&ajD&d+>pgc}cmT29R^{ot5$oM=y7UaeYOk7p8pWEd^ zJ6ehgV(#R7SkvF*#aJH=r{>Pr?5VE-7e&qudTvXC;lg%dr`AKkUtg(?Ju{ymxb1bl zUE&M4v_5EoDi$lV#%`!$@dvoKph|_|`4ePQq5)p=@p+3H6>Q$wxKY9SnP&o3G;|@= zCaP%cNh!pY4B^%N1ny+)S*nereCdL1CoQwF(pz$}{~d zM?v9+j>`}V9yxwj#ruaSzt4ewPH;L z)Z>teVlQiw)F23WB*rvTJqd3}c_v$Sj{`e`ZJWUS*aXZN<#1*V?Ez8mYPe12XJ8bq z%qpAxF!*G8f%jh-$^Wdu;o}m$AY&;y>hpmGxN)>tyrOUljP>sBQQB1pV{UMtqMsiH zKMzLr-?M9nGxfn=LKpgg|4bp1)L1Wc72fY~NNpIr61L0sEMJ8jcH%knYru<)<=};H=ERBp?a+3nP0Oa_1%h^Q7#oAzc@|c5mrsy4MgW zJQ-qj=WIW8((GAMn4KU%aOrQYfps!q==e30H?;ts&P(S?T`2;pQQ}tXQcK{luExg9 z?Rdy9aW0fTY!38mxxxE8(!quFT})cEi{O5HM8`s68r=S$VSL^EELeScw@knh!qI~l zJ)5?sLGi&q*(dyRpnls!SR4wH`26qeq zOPb6-e$O(2V9u)>r`4K4!|AU=zj~X%7v^;rmmQTL%+UF98tG!$rF^q`Nzn|QZ10%M z4-A8q)Z|xd^RDxVFZ+0eOJ8MJpd}bR74xr%K=To;ZTvC!{Cg> zj&rYwwjsb~PPf;eJfH(@T5XpP3;|Ql;P+y6WVwu5SQJI8B`SwmQM7vSiij(UQh7pB zag=Jy&VZxTUpsTl$tV>F%y8pqHeD$NN3+MyLL9|bP>4Q3Q7k8&ml=w3ZN8osMN#gL z7vnbwj+65+U(Mqa*Y77JmwRV1qhlPJbKR}|_^%#F)Un#P8IVUm{+V?q^>hQ-lT~tx zZBvjg(~5`dUMpy5xqJ6T@Bj?;I3q;b3gPAhM@OxcbmH#BsDAi=s)D9cct`U zH$AKPDR$2jMm4Z|9-$sdVEa7v-)uUv&)fg7>LEK`KEMy#@k>Mjw%->{9;`w3`Cg|ucc#zukbfV}kjY{jG7Qaj3{qzAn}bi;9$J6%8--Vm*bY1?8i#$+?2nu7j=*&N z?pJ{y`pD=-tkv0YiqsbkGg<{Ba7LVvKA@T~3G+Tmi`ZQ2f)PUc2}cX&AjN=xYC72m zI5wP?zW@9T{FeWm>JAAiG;;`37bT2C>Y2=r9pqZxA(z!2rZWVU6h=>s--`xiTNj#z zPmI6-@5<_}jUv$fZKvc=Odp&Yr@3UER{)x5D-Q%+B~2=QiJkGLl^~pmHms+6nhLLm zyvb7Knuj)d=XmJ6a-mm9@+ZR=OYrA>(fjWcxv)G{;+#qE61)*M-5EJn4f$lc*{%yO z!P8IsK3uYDgnj>D?$ve-QeggUywx4S-_zcv)M`w#xO~3~k z-%7qr`N8An{C6}LCt=7$+>&FIFFa1sD|KJ237mOwW|C{c9~cTKUXi%l2JCGEn6H15 zhYw^jpHkAd0WR}^zNEr0K!YV%&ir~0aN%A*k--oOhW}{PG5qZY&GH6&pB;+^T)Y2; zU0@&v0oA$^E`xiew)LSdIO{XI4pbi3tX0iK<#GDxI|ZnGo>mnlh05oW;ab;FnLHq< z2A9d3wJC8K{q5}g?fzsLo#XjwTt+t+9({t!=*A7ExCMJX{|#=zE<0^sM6Fo?!tDJe zf~?INjPJ*_Tdj*w7}ahsw9n%j?JGzdi)yr5Mtg7#S5!O}*Km`q?+BtA?zuUp8C1jN zYu3Yf9W+zuLwMcUCt(=Jb8pg|ML1sKsyW8_Mn^m_&Nu(>ImYuY@SS!g5T9o_9D(`1 zPUYGP#P_ienWiDW?|95v%=aZSY51erU}jIxie|$djl+1DNZ4x^p>Oh zscx;8yqfu??o)|OBMcX9KJ4hy1U|JD9`N4N3iXqv^h@MpK!)07y~zjPfux^C7LR8M zI95pv2xka-1GaE_n{<%|z+CXJ?Z{9eJpOrH@z~=`P^mY1f+;x;3|bvL%BE8VV(U|s zIi0^jxt^teCE10b@crP&PTmCgzDnKo%n2XBqH7TDq1gk((i+pg^nL+Nm$oyqr__P^ zGk-EQ#?!#*zN=O;R7T)+B5m?!Rv?fks&u&mPqu*$7yVsBkCMS8)tl?Wyx#%kWtZG_ znh4;c@MK?>W&?;ZAD#MX5(b_KiM2l+90F;jxtCg+e1Lh|E!Qv-*YCSrD+hPH0Li>z zqL-I*L1X35pNF*r0R!*p?zK0AfJw;U^PaBvpou4BU&4r7m+W4gX3vYLUjTVmP3inud?o(Uja4 zYZnPVJ`l5}csK<9BO=;{PWge?G;HGfV_ksJ>uU!{j{vEUq#joD4TG@TgBV7?C!&sF z^gOP}NfKTsz_ec$rq^lMN;DAt&OLk|)9;L{OPFT=n|lz`?9v8X8Hk4Wz9@xh_@T}t zi-?9_?q3x^G(7X#+9$N{KYKODjrRSFvKs6UP`wGn{y=M1#~tJ^a2@f-{(@V|!{!IP zL?tAirD4NB_*MWm98O=_uZ|3dsGH(8$Z$9{=ztA}cdjBY(fqe@>&NqdpZ`^?7wT@C z{zZCW$ms?a97khM6(GS8L-`Kt2RW;5tRKQS|6M@>B=nRP79f*Jmn>(9>m)2XWQYZb z-?Ag7Sa0^YK}4}WJRgXu*Q<}NVCvPts{~`S_Db(Z5jC4Kbp8Jn>}B?Ch=P5ktBfhy z2gDvnMA6z=bYhBDu&gT|QMAFaS9?(1f25>f8P)v{XzE80=ss9eMF$2_J`?z0fYI`8V{|Ye(AxnY9Ee|wU_b{4#1#DS!GY?SJ*Md3z`EfsGeo;n zcmqlql6J58J?4$*_#B%|4MfMA#%a|dx_%s}#*+n+n}HW+QQWw{iRXq&zF*W?-6e(%$H;_X*lS|tc=&+TOf zczaIxvx5tv}M{&HndF_ZxFe7oi?k`_=nc%=!5x2A$lV)Y^jnn zP^2AlKQ*C!^|=!=suz4dDA^4C%Zv-W$v@2`eZq9nxpK(pZnKYWWf*23X!f?{ZiZ`3 zwXfc8>x3u6D01fGt6_!3hhv8hjX=>fsV6l&HBc>Un%ZzY2l8e#PoB_@f(^uI%XZp< z04VU@Po*`H%Jf0Jafy5ihv9_DRBNV>x%+-eIK_-0HqZq3IL~dH zhCrZHcj3__uql(X%+;BI!{qPy+tG2rC;iRUim`6;|uz@IDwR zBfkaDgPK`N;PZv`zUP}w;2Kw1h7wl;)G9LNE>0jjp)8XZC=x!w1AcQM|NKaHq|oxU zgHaTS&3D{ju;>L(=*WaPHx}#~B_1Y&?m=L7t*%W9UIFV@88(aaI>AzK=!XA#95_(J zpI#a?2$;aiR0fOpk5mk|WP>aZ?|7jd5Hi+l znr;_&0)h@Kbz!G+piewastHp$a6DtGJhYqxc~Y0e=jzG$;L!;D zABS@~)3ifEq!n7&>Q}?F%RAGyJ9ogJ?zdrRe<2*8{Z|!8+Xg#-g;JzQ=fj@@i7FnR zO;9=FCi6RuLb#Fi&~eMK8D4Wa%B-#v53P5|O?R(Wk@b5D_~>jP+%)gK^eb|RgwneY zj>C4yf6yl+3LVZPtliE0S({nQ-d!!%?+Ky}@#x`pfqum*%--ajlR5cB>&PyD|xz$9dR zAN2*cyszWF0Glz>4b&IdNRGzKX4G&!6IwO}TKN9IoC>_c%W2LD_jCenx75vC)M&dE zb`Oz2+v-oBE_t-A-V0>L+wOk{YTD7ZtF54bw_VHQrNU_2y*sScfC38r(KZ}V40yEZ z7?FVnyOs(LG`_^U7otR@da4~KBGrfJaKMpVM4yQQj>w!h1dd4NpOTks$cQBEt2mBG z*8JFBp@>9r`?NVK7;f%k3&&6dGb-LygA6T+UBWqw&L$Og~gm ztQ-9og9?guA{Jh#;Fu;0kE4QPo*IWDDmW_hy5WK&vmL>Uu26v^zrLlTBUE}GRGjE) z)weINUZbm3JN-OJ(WosRks@yeQ_1r<0@Sos$vr&_gQe4`*dZ34eD~M#=OE^ZZV!N zS=8kYW(t=_^FAoo49|N~TNa~vUt-SQfabl#dWbHX_s$9b@VrkK(+@}U{y^MGJnzSb z_u+Zp77&c)J(P=y#pC`EEJfp}N?n<0kd z(d87(a43vyVk9G4oev`!pSmu7Kum{Hnh$0=>NqcaMo33$Sadl;I!e4)^^l%$ViLl7 z!e4v>>j_@lR8mh6<1k6CUl0k3OfNAkD8z=nu%H+|>x2bG_5CkcPGC*;*g`EzWBLM+=9K=jye= zp8GvF-rJ7>4gQQ-U*c&ElyDUO`j>hT6!6^&dc3mo4PD4SYFrWoJNA>3I#V7EH-V^ACO=vVcHZ#f~7l6 z7YmlTD_&Tz7|?fw5=gPo&ra@^>=^~6Taa?itQA~{7Ukt%oCZnCQQGHq^MUN5V9WBa z8Q{0iK5-}@2M+C~R+p?91_q^yFQWw;Kq}wG4l0I8@Z;W%ynCZbpibwFbxt@*Rs`B< zx{X%?*6yEJ5%sqPU`2E-vK=d;!96VmmPCA1x|rdxA?R%o!;wl`j}=im=S7TU9OL|m z715iM*RdjEtXNb-Oh>R!CRRi?ijd>ew=T%BKeUlEVi??e@g#UqRTnC#`xO9}VbI>| z7Bq2|Y*26Op|}&-3v5ImP+D>K!tM_QZRcHy5m0rw?pt_x3amQ&{Ed135Ga0m*m8|EnEHUC>FqyiJt$`DQ(K!3IyeIT%!1DtDUyMs zUFvwPFUb|CDLb%|0cW=Lb5?DI1=ua4yZq@=Gi$=|!$Ha7D00y}@q`3Op{uqLJglJvXEosbmMRW(<(LtBW+FlrH5N zq#0Q`7O-ZN(D=V*q&Jzzn(_8pMXg}!rm9l_+q!SYBi~pANUc$W9!+BSAQ($fD znhjFD#U^ojNcHY9C>BDhH{6f^KcsrA#Qs$x)hkEOfmLruEAIoOdj0JzvFf$!6H&fG zs$Qj+hFJ9Wn=BKDk?K{jIy-<=ujy-b5p)ZdHev?fg3W#|jBme=+w7A>w_o3~D<46( zUsIKuBhc+vryM7A3B}7@ahXb#j0~4dla2w4Y8}k zr&nK2QI4Y1s~gE}_;hQMW%UL%d7kIS+%h@eaONRMzt!zF6d)=!7p6oto`oMHh;4CmZpFBZ`Oq`_Y9XgGp{2Ib^++`FPhd-4eA-07AX9PkEK&zS-bG zyTsuJLE;;%cZ$zsyifvs$E}U26TAW6xv;ld>!n~TKvFEhE(4y(y78`-qYi8iRF;o_ zgs{u*>UY7W_rNoAy5B|>(Efz$#dvk>M1+J17!IOw&3TkQfD`IpV1!uCq@rgE|!Z#zNVAL9X@T`EbY zvuXu5cq;qZtnR`s^OJg1d$YiHb!{%EAKE}ygPpRPF9w7kb^57e76QyIFS8Q)sr}&F zHdTt73%`mQ8d90pMO(l%{-O_MDn2lbn(|h>MLc;Ipeu=4#Rb&z#v0x(ZUr}=IJML0 z+rnpquN5qNvVaKdbBWgFN3ic$fJ^6(6ziX zJNodoJT+8PIiO=7 zy4`eI39j4Km*&-=dYyi3KCag#pEMsw^*Z*Wk8!&$Vu}{m^@1J-;ksTmK_K1bgz~5|(g}@>@yMsRCM)g-_HGF(&v0DNsqIc*kp8 z3P~}MPx2x=A^fISaVO-OkUj2%c%3?nOD26mdvVD`z1N2wl}c9heJoI^#MUUG0hLmM z&mY636wYNcd>igcSwsoC4Hx4&!GUhU*~e$%TW}J8!?e&XID!~CijTpaj@HD-Vlj`#nY)1`R- zU(3tH`+lKG7~c0I3zt@rUof^{YK8m)^?$rFCT_$K{2gMf0vI|VSD;YCui91^n-lHB z^`ZkRJO4JmHc$oI&PeDfe`$w)|Iy{rmtA5+zr$@w593GTvS5^$hNL=YJiM*6J#Qi;1vVMRrQcS03kD~Q>9!F$ zMR1F~>_`%G1aPRcDQ7tw0pWs#?OqiK{(&ohiuR|#dht{t_D?0?aA&^T@h3r$`mLr+ z|Aj^%F~+EgI3Mcgj+Kb>0e9Uo=Og4f@)>bHed=Eg5a*Mo-kFLpAFmFEcL?)|Q>Mq5 z&$o*qlLX>?vKU$d5a;8pJwU}qay}`{F)E1j@vdaLgD{*NcgBkd!#Q!Du>oN?hj_98 z!f-ggFpDA#r(r@u3t>3J$rs)s3@5m{Fa}{bfB)^!MHr5Yeh0>IpWWvMvEWY)^nFeW47X@x17?<@UIh?241D8-aa=<#is zk`eUa#+1xf>L#XSwwNg}CDXIciy@XrJM=Kb;=F4CLoC8+Y8YZEJWAZd6id+Jk86lx zDJ2?~5XBPyj%Er`EV6%SFvTLsdmd9PH%pl`5ykTO<26jN{0i5@nUBS%m-#63apaJ~ zkV!AKEzWqj_cD->Nju=w8^aOLmWw#fjQ-p^fHR{9gsTS*iS8TRz#&nv>okT;-hZ{Q zLXb&UH5+C?C~E9+y*|Qm^c7-2K0o}084%OMZO2fZUg=y7W(68JkMUoUCV&sr2H4D0Zm|o>Kfk2*rc>4Qxv7&u>;hrc{{l9_(sy zCEVGCT&<)IX-qiKf2XQKgu_lYn+-%b3_kpY35Tf%)L95{xct5Y0}f3a<)H|0pcZKT zk9Lma{}fzV4Qwp~A{-i>8FLWfK-2aY6Ap0SqiF;n%$Q#V2tW`X+dqQ<1fwHQ zRSD072yE8!iMO*q3^-fVUjL!%WxNlJp@BGes&U&X5a_UtX*4?Orx97XZjhbqp(&-TX zTTJCpc<2{L_VaQW$n|c(hdUOAyE4V5d?90bi56=y-agn<+7$j?w*~I1D%9b;+XM3{ zQ}@fTk#__yoU?b$?Sx5dXTA(FHo+#AJo5gHNf&h6IB+adqYDNX|IW`EY=<0d46B~7 z4epLRs-yU$8JrHU2y+k>lMwC%D%JI_TmvD|Ze`feG&hhd^a4y(m0&p(adrAZ6 zfXz!geNYZ~U|kmHh#YKuI7h5syWozxWJ%_Q+fbLxRQW&o>#r)fp$@17WLn&My zYyA1H6_v+4t~fQIGFssQc|TM}yAgXEm(RrSOEjYLnR@E)hk-F<>#bM(94fDw`S00} zw%^u&p?Le1nlK4K+wP9P^LX1$j5Hy)-3I6uqJ@{;e>)D~?U~r4IE5pZ^>`~Bu}tV6 z}Oe4u#zxuq{UP@G-RgD)O;Zl^qtE*>kif5jJ$m-ZQy zp$o^)JwqhXh2+0l?|!2T$p?9NhNH9H=Pw^~Lub1`xBCniye3+~)j$RVbf^34Lm7Oh zduZvYA-dE3hrY}X-Tlr?HaA3fzbD(S;XB`290B;wx7Gq5z7sCuRy2?9gxBi+dVxr& zKUth#5eaqUx|}T{p@e=+14KfBTNAet3H8P6HzuJDF(utHB1jbKm0LPOp`c)&+F3+G zeP}t8gGi`Wi-+G433V{}0w$rp%<5l9$7n*R9^qp&T@@CZ==cmn=&u}fd`9ZL!E1DU zCTl4TAD^in$kIZ`Y&3|=htM&bmsGL%cugup9f6M5B)tp1N}%gEVlSWK>o*zs)+(r_ zqCx))w^Z^h`EX06d*KHzsW>bOZ9^p$hWBc?q;j~BvK^IFo<#6{L?xAHVOF@L^5R$u zE~)&QH0fa^ODaF8s&PrhBk~!>cubzZ#~6>=<1qqrJMlLl=63cxNIHVJo$osaFt_6% zdKz;;lN4&01KLOX6LUYJUUBh=`^noKH-xyM6-5io4N=Hs;}*=9zh1Znv+yld0NDOIQS9-4GgzO-^EM-SLj{eBqoDh; z@+dgc^^bS*z&H5WUp2y(egc#&+_v07#(9wNlu5fMG6JrC`SU!E>~<7?jlwmX)Q&d- zsAh9xha9fiq`#p&i)uEK-{^47=F)!;x>3zWtbP0hs@X{24V*$Xn}_r9xMt%lV(5Zu zHU|CH5oFB<-f7>$Et@#u6!Dy*eyC@@YkI_L7!a)m-v!3izkn~P^NlIwFmgNeKa$Qn zp36Oo;|(eiB~b_=*;-1XM_Ve9EhI@vnTZNdp&_%#&Q7TiS!FzBQ?mESCS@eqa-VbV zU$^_;eZ6k4@9#OEb3X5P@~-vb--+~w6k)Md%|s;68`I^9vDDXV$CC~>wGncIS#`Zh zjYP2RwSaAk4TMGgj?xo1Yl$D)1Pk{M8Z4fjuLox~V;hg)nN4+oBVN^XC{1CFM{;rp zUey$|y@snAk~l7?{t9|Ln&$)YPA8I$k-p0*q0EF{;#tlQ4=Nb$dTu*$Vvoo6E_2Sw z{)Y$x_?Ss8gmx`$c_b>7SPl_nznVft3V@S6%> zl9C;2)k&me6*%9{>Zk0q_NUzTCA*0o2JiO1RvV;>^bW0V+0#Q*d9lakq4LgmL^6yQq+w5JlejtdgpS8=N2EjJRaZnhc&euyf}YUh z+7bGgtbq_nDsH(hHbPy!nq;0%>UUEtt0{K#t}#m1S=0ZLX%khvXV1O7%VU)5bj8o> zSBj{^spmq}o{dwUwR@uydn>6DQ{vKnyK%}O?qQbw?st^)LJ;4#V-r-uT4}LX%S^(; z${CADl;+SgC?Z`GQN=BrikS{>+0vGppd8DtmbRnRgcEawDhUfytWtH=G}%Cv%G)Clt5oN&5GSBYC9^+a9aO2L&rME3hV#Ki zivuE@v_E$l21jP|9JsEtLxf|{zuN#J9M9#4&qIXMZ?i8DBAolVl9wRD5mR%WhX{x4 z7Cm$tGMpopcPt^p`RS-Q2pLXc;ksSm|KW0evU`epOglc=~Mg=SPxmJUAx$XF}tl;j#uc9#lvCW}xxF zb;|^Q8#=}%@wcIXbr1fSgdZ>xgdY=Dc{^eFG5Y#2v=@Gi3Vo&V$EC1w1^&43`0C(~ z)7|NJHSpt9AoY0yew?lgODVvQlSQFdBROqIBh|mnDg9C}rUuXTzjW5GBN#pRx+w9L zP!ff*u>ne@#NK7^QfXp1NJ^;2tG2$qq^8=@t}lyzdDGFKJ-?{D^ne^Jh-c3)Dx zeKxt1NIVju>)2jQRj>)^)ahgq_3MPMKYah0+OaSfqO`BVOsm z%(5#KQ8EuMGW=vcPi*Wze!5dSi<%O9uK7hSp7M~~_k>VspjNetXnfHwq9!c7_a)AD zQvDZOm{wQ>63Zlr*o{7)r~y`TDk(bYDRq2JlJwH`^%Uok469nqpTKfU(~)&q5d>BQ6I)$QBgUYr89h*sKYW|f)6F~DdX2Jaqd=q6m|K{O`{u+ zsmY6Wj=I?&sGhzR4$5!dQjG^fboX19mnnZZ~mflV$twcNx91QE%;i04p257g0B0WuwX{ zg)5tH<{w63Wz!_zk^?In_2}&G37Ro|^86!YOgjo`ivyJU!%Q(ksmY3u;sQXmo;;?G zRO{6roRCsId+mQp_43HX9-v&S?7xVV>*u-5NVzukY(mQQE6(90_`qN~@C-jN&Zf#> z?|0Tl2YbJlytm>9%{skB{Gj34aScCEbA59vtuh+^(mb z5#n|kBKZ)X%iXZG7U;Q*$NUh#>-C)R3gCCu>~A4{H>2Au3Glnm?pDnKV_M$46B*N? z1=&mhrGEBSX=OJ_Q>g>`NTgcVw>6yts&(tNgRVfSE*pNZ0VvfvK82Y;xqkezG7%`( z|7tg<1LfMAd({@8T<_d*<_&a6-|)rWgbwMvOcIt(AC?MdLFx3*TQ6%Utsa-We*#LY zqtdT#w^&b#|mJOv_c( z+i<)NjW}!wLUZ7`{$7{~!uPAB?;*=4=D(PsJ~Y3lpoE%Ax++1A{>LMe<2 z^H>wprSh3t^Sx8exAF@mdE;1ZL~J_6!!G_=#xIq+SUbAhi6@r|_LyC<$U;$SC!03I zHC?`*k_BATtzQ#{*K`*eNY)ZKr#s6%_!7?PRID5W;GC|~)#59h(+w1^!gIQ*acea= zrwfw!w+GJY(gHOP!8slO3lA1Jrz3(HEC9$y#knx)obdK;)&x!SVPK+R&jT8Y%G<+f}{&H8pl9;sPf$$KY&niX>7 zGg7mpZ-jIMHH)T4JmVl#xZo@fF#-*7`91bXvJdRK|yG>WJ4ETw+s3mABOApFAqDI;JDpd zvk#BkH4{co!g2fRtgU$5F0pL~*$db0^(I4daNVBUByk0<+e=rT!s~X~9rL4b-R|r8 z`!QU%Zxr$ofOq1JkqN#NE>8}vfp?r&N~C| z1dCG$z7w6DoAcnEU>PB07Vppxg_UnJ6Fd|VR7?(c}_e*n;Vm6kLKW*b zIlkX@9?&XXcMthM{2qP62;=wXtTC9^OCNed=k;HRa^m$kG#dHlzLZ1W@7!7^2=V=! zP;rd!HJ@Caf&BkpwzN7l99M{%VgA4F@>3xwI8N_vj)7Tl)USk|1?m3ZM!lW+Fg%{^ zK7zwzo!i(w7(RbqyoSSPxkySU43i(GjBuEA?GOlpVN_wp35U^5p)mu$=wY;JY!?or zE*^?zAg%bw=q>`%3US3vIv}}tY`f+zNG`VU>pcZa#)1hI>_*a>j3-VKonXD_+4tuS zSTBC>$W;YxhPZDpYBO{)UzdV5BmH|LYBP+<{s&;U$#}tlb{m(7v0$*_Jh4a<1{;n_ zU!4)ybY^3m)`Lx_&ny+~IJ@&t6oDPbemAuo>^hx1vuq^jb^et3^@3g}=2&h$=yk#b zw)O$J%yM4d0?1{j*5s`Qa+!pz6OzmB4Y<{+(cm)AHzf!zn-e;Z;4;o_8)^Yu##d*& z3czJT+Fl4QOR!CM1aO&gZaIR>e1mQ!0p#kaj18Fzlq>y!&4&StKDXbic#!4@jt7G<82)Q2sExc5PBDY#s&lG2sFO4v0n#( z#?+Jz9{`P$^Uj>{t!X02CApjhMXaBR^f7+#dc!Wl%7MZ7OW8|`A#ue>)ZZ>5NN2Z) za#+LHl%HtTJ{+UuU}B3 z=LgRKF?m7c23}N*zp=go#AJ?6OFY3)KO*s*KH;G6f8D0=hT{|e#A>+7m|5kBCmHsx zfn{*hai22@Z#o`tzSjmf9fKY@vOr8GJEHj?>@6Sgh|B%N?cl_I&M#BUvvOSZah4g~T^PJ$ScPZYW4Y=C@XZd!}v6N^&AbhWp` zR6`SwZ}HASlDS?^^s{NpAXmHni!t&^~%O%cZl##O- zA!yo6txbMI9x9#qL$S&XXc!Aq)R}pkl~tR5Q@2$0ZWk+5QRZ6f5GrlJGd2%U=~t&V z*}zh1Vd^$6mHuA*V+Kp9x$)h&l;RUglz}Ca6(NsHCVzM5{jj7;t>1%7s*48ucwk9o zd-EACsSa|I`Q6I5E2&@cT?b?2e^5^9x%)2Zwo^52+ONOH4^hECS2XioqXS9NcN=!e z4^SM8k38jsYpLPyle^CA4^Xb+d1J>FIw_^!V>(-02dD(6gE0zpXY|V|$>*yr=vnex zXFfoyn-hP22Wa)kis4FtRtvuSh0y8@a<8i|P^+WQ%@zT*TDPRw8mQIYmhX^Sy?Kd1 zC{U{l19*{Iy)wgO#FsspaG$@8(CTUaOMDPe5bQzm5Kyo^UV#Bc(NM1x1QaKKsbN52 zWTb-u#q8{}I}lJv_K*$`P>^cdSKow$Vu_J1CKNyN+7lt6Sn8{V359>)lq~duncEIy zFW6dCFdTZpw@)3%UT`c~Fq8GL?LgXJbrFU0FYFPJ38`Ol&pew`W<337Om;&teP*B>t42`+fk!?Ngtf2CH3F8GBn zo+y{!YMXHzc9O#2XC%k*TT8{oB-c4ILBuk|6l5nju%$p&e-)!WC}#YpG>rDCnxE%4v&$%s zugRaB_jFOirSc~(UVlu?v6C6|sv7l_K}&ymjyZi4l5#d^%i}g`O#pW)@77OL-O}Uv zg=~$Kap{wvb%Y1CbIsJK)xQENp8ugpcy}5Vwohc-fvKG`-d4!tUtLT!8-!2-#r;%I zL{NJ9hBPW)H++6`Y%6u}r?!3ON4n&8m2JY=u9u2YUHU8kA6@MrRY!Zi7j#owt99SP zMxw(l2Nx2nj zP$Um?zr6v<<-tP*k3qStRX9a~V)^Fcs*|8tK2>~P0Tj&&Rc5GY?s~~y1|K9kZ=c}@ ziJ51d8+?EqmHIskMg_~l<;L&<62V!4Muh{l?jiIC4N3I(i{KZ{4nIr$f+;xahF>rd zVRPtokpK4%zkoLU;KAPSJLk_l(EAl;e1Wx`&3y7$%SnGdjkO#-uK# zVQ+X%mKN4}dK4nC)?>Zo3EeX$2Z^V9ZeTI!^QrbIh)@pJnPP+@xqTx>CO3-SV`ReH z`4=OT(`D9}m{eT2gAt5z!dZ-9lx+@Tf`CB8A(0;h1k0XBqj2E( z8>#&u9LUI7yaU35yBo$PKv)p>P2m__sigG299RjB%1p__*q{tmGW-Dz%Djx5*r06A zoqq!j$_<`@A4uqAz9<^QPG%mZGIE37)3rD&t%eHb1t|^eaGv)|#17|xh5-(znm-qD zIK56^lfy8oC2MC3!|1E)R0j;Bj$_I=j55`5>cDW@qJJBQ+lq6Ik71bo-p#xZhS{%0 zWh9Qnfd*PQ4om$mS_+p+vs$b1Qi<_<5MCO2q&`=*Je83IIaIv!w8L01zgp z>ly%n@Vme~0tgNC0!08osA`;r0KyGYqh$afyvh1;82|{`*T)s60!)M`u#~5b2>MLQG%~i^Z;q?w(Q4Fui+kz~ZephZ`L-$+!=8XR#6Tc22 zWa88KafC`(lnEkKBHcI#p%SbN*0}(c$X)RSAr(ywF$k%cZ#aQaig#bL5lS(^Wrk1- zuS-0i0BX^l{T6jTmWmFb^C1a^;=kzcsXcQS{XJJh1km3Te(Z<}_mB>itn-as22Dlvo6ISfR9nBSMu5B57-d zzQ}Cs1JL>wtjQ;QYDje-z1M-u{3D1(oOeR82 z7iqWC8|*QX`Mh{^1|pd~8(v|?^I*H*b;x*<+BRXP<7EHlCuBP5Vh%{V9VvVy2$@h4 zA%-E--uStb5Hg*)&Wo9nrS}rNtTt5Ga0N1@Y4-i30i0G>RWjgd_1+4jPjFhjG|L@B zsYyjS45iAy_GCh;b;6+&Q?2}gC-@LmelNp^C`juCK19q8qnL_aQSJT?50SgpPZvnV zb}rG!hv;1QR(yyA?L#qDqa4Z2_!7P3?$(7|Zu{(slaR}mP-C``^KI%Z#GEhqq&DVy zoj3N0K(6PZ9cu`=UR{^X2gvmf-;`c}oNzk&@ngsdi^~MmAQ$}ioTCnMK_0$D1;`bb zxY}W^xT(u(2o@TAI`W6C{(4k1ChJ27~LX2_poPZ!|{NLee=n z_7RiLWy^_J*nmj4A5({9^I8tc2g&HAPe&7AgEDAtvkIctjz4lA9dV6AN~0_8OE4KhefxN|-99v~PVSY}N@*{$kQ ziYZ-gBZT#t#4B=3Zs}Z*ifyqeKM-hEKAwGSPFbOqx?#D^UexuwR zOhS<}`*0F^eM6)cMyuMwDjcmUEh=!5Y7hE_lT^E@^amKl{)ucse~^-_VmSMI(vx_Q zl4cBQG|3dw4oqkX7ydV)?F}zK1SYigt;f-X_OSZvdQhu*zq^KNwZy|2s8Zt?b3~Pz zT>l;)P_b>{=Qs{3wwJxBk3sEL{PaR5sNFVkxvmG5o3x%2s@z`4mbP<|#m*GuWBbAaKhKJl3p2gTy-#ZXaDEXqY% zKLEvIrcH+}C>CY8PhJPbV$|Bbs93xaxAHnD7M0J1p|h69GMxNrO5fWceB3=bAUb}8Y(!lr$sEnY0#SankmE*4Y*-s1&C zMjI<$K-gBV!HWl(wOj1r;^Ew!SR-6aY|5-U2p1FkAIS-Ubox5?G)kvi=VPisI-P9f zj?(Ffh$JnLPM_MRY6a5irPp|N$h<70GRk5!z&U-8eE+r(q|8;ED8Hyunn^;{id+_jO%NG(`I5-Tink zQ15;ht=6);L-61rCBgy^4z_bi|Gs*3igrouagx8&Nkqw>zH;WoAIk2PgOkfudj4On zkn4VG^AN=u6K-bptD6`LYT@MCGeJ!o_E+zuyW0wSA`ka?4O2W<0%ZhubrWST6eMN~ zMkt}1`r=~`XsK+>19jkr9h_)T^`NcUpFX4`Crsn&9pr>PU$+rCVa*OgXw9ArQ9^6> zDc5+KJ4Ut=;q!`jK%0F{jT1p*XZRJ`r%!w*O#f!-f;QVpwYcW-iE(Oe8!5`1PhZ7X z|88#D{+HUxRQQ{ie@0|9%>1j>_)8sI+Q#c|Swh6B1bYUrousZWak;yTrGVIfQPApW z=pU>KE;?3J>lUWKSjA`+unOl`ZN=KZSL$qv3SI|h!l(Y8`Vgy2&s!eYQ-wq ze@Ly+EOtR^#q&)BQZS0AT#$nCqE;{fs28fKNl3jY{w9H1?tsYaKS0ZUVflHj)`$k` zM!$~x$0E9d^`0#&`$P}*#lS*2wB{u&n;kNVzLzygD3^Cw(jNE@f%Mx<8SheYr9P``BtY(TU^2GRTdQMSthRw%(N zcX5ScvrA_J#%~&&ItJsnTggEjzq^jD!||Kpb~BFO4yku>{1(1#g5x*4ekqRM>i*m~ zet&wyi{rOj>=_)t$BM}Hy)b^StlBI97L3*gX0%`o{*_??3&xu#yU>E+UK;iQEExM; zCuPBcp%=Q;3M?4C!E4ZhAr$hb2`m_14lj;_1;eZNlN4AmSPd-Eg7LoSM=4Y~MY5M{ zfJ$eaeTgbmI(! z{=d`tM1iG~q&uBwWL=j-r&E2fsauV=i*Q$uG~TY!MXZxQ_D-g_oG`VuGJW3DNnFbD zKdsQ0LUk^?OxmArCYJnK7?aCrCCD|+l7rkLT||H9+ku>SbfsEzYf7VDC*4pu>FyBU zK(I%BKHfFjNxW`jLV>VMwFnA?cf<>#aM<@k9SVo@GY*u2uvkRlCJKv}aU`IC*up#x z1;jsXq<({auDga9I63Gzg>1IUJP)#cT0~TE;sRv zI^uQ>bE)fq+g-l7Le8D0=lbeNBRzNXfZ`zFcN>e65WgGex&`sO=l|#L?#vH3jTW_W!b|5B%uDdn*-WMxFQrX7 zWAReDcrhL?rI$7g3&ExInb+Fp@U+fJD`~*fy5wsEKE*2j{b%7R?pgZ?pK>enK77i# z(_@z&BI&m|>@P0~Z}U-&AGYwe%l5s*w|&L!{Gaf)>j$yo+io=QJ$d$b8P&JVd}f-d zhSF%|J~;lhgVK1u_3Ig_Pn5^f`e3bfKdIy$0UJh@vWN-JK`M87Ar<3%HZgdC{^y6? z8mH7+DVCULMNx8hs9k#uBFRH$FDRwW4J^{J@2C^>o79|}8$aXZUFK8)KV zID;^5kG=c^<927QVT{|k)ABKH@4xv4<96OER*c(O47o6Fzj%>71rQFOI_ePNaCK<< z6CfOT)#MQ2@UtlDIv^ZoOSdD!VcMUpM1})v(GW5mqExw%;jqU@=p!&3Vka1o;V{*9 z02vNB%kHJZ&oiSwKmI&(c|6(3#O_s!i{(Hz&7!iwh8hK*R! zT&A-QE1JyT(%2!exI83*fyGw2u}ctesO(b2fJ6P&`lXPNSh@23=11$ z;^A~Z3?q}cib{-34vve=LS(Wbz-Q^VzDDZ4s@5nd844dPy$ni*%P#DwaCmpIf)x}F z^{hVVfOx9)QWqQ$UWTnHpmfmKvG5R-4hMVV(FxI|+G%IPN|sW$C(l0vDTFWAOe#nr zBraXS@)>bR8_Q>YwQsPkHsr0c9Lj1H!g~{-?H1~*ify+yuFcX=UR&n6DhJAIi3U|s z(0)66QOE$=Z$2~AywHBD+wl(DZ@Z%I;f+br*GKhABzop;hR5(#YItO_q;NYP znapl_F$+f~2Vz6;$YidW8HY(ZmP8yzzifGf!>FsvavVlq#+2YNYMZeR*>3Ie{fKt+ zvob-nn}y6`E}-4c#Yuhvv>TU8-cl0SZehhN$aZ_*oxuidx9!jLZv)$nQ2E9SY`4!} zwSNKIZ8~GX1=wyI8GmE&ctf%lgGWYL3WLfs{(cx#{)!3Vh@_KFegQo+rG6T^i@{|^ zMuI#9mp7BnPJ<~m@=XlM2FWLHa04cvfBuYP@;M;SRt#lx-?AR7@o5^ZW}%7Ybb@7P z1C-Ns247;k{o=Rf`Ot1Z5qAvB>cuy%Vp;v;_VpW~?Y{j(0Jhz?o@d3jyPH;3?L0>V ztz_(10z=aN=j8VXph#NsLKKxsC6Sh>RB9AcL?u%iu`jBoES)elzk!OSHD(K_SQ@4N zS2Vf*N<>AI^i?TTG;Q5wv=S6eUMX?xK`*9>t2=tr}EP|A=}kJ8w?-b z`={}jNPZ>1#!kD8DDr3Dwm3&qFP#rIv37M5o?2VizTp&J^0r~B)-{|GP#$&ieq27Njkfe=nS*f-5}>lk(2>LUBoQ+l?X+ zm#lD=LvhLZ*$3(%F0qj7zzXKE3Ms5$nh%R&Wpk^=uP8vdrvLszS3YU}Z+dIq59oYO zFT0E#PWgfbALwxI&g;PrXi!1~c0dbDaz>?WlF`oZtQ(pt|yzZ5>)6gSkI>5CR;`#TF_TPje z^68a%N&qYfkz+1`;&LKWr#%#x+m@Xs-$3(O({`miG@r}0c4L$I8vislnQH?Yu?g)u z_v9lqp?&>pn4k%LXE^b^EE{5izJ@UsO{?7Ce^_X*2{~ZmAFNu*+SxYh5gKxf9*x9ihwZaI#$!YBzQt zBe8bl^6ptF7>^j^?qf~o<*m!k(DONOD22wPso@q&sO?mTJ;2(I{;-Y!^nUn-y0G`N zsi)u-7?d^|3wnS-=?wY026cG>jBGe4@VZN(Uhiy&84ea_d&E%B=kupm3-o+yamgJp zXk5O283&DJy^d>OP+9fP1P7JNPD`C(P%+xF4|RQt&rWOr-`~5mSxfN!ah-P31wY^} zhO6iYBqy$!eF4FsNoG6>24&8fb5C*PhdDj5KK}8n}6b|0?s5k|}!93>& zTp%3eUHa)bXgZI)IEI=|V?{aISxnyzDsJimO{ZV+ol?+r=Fa`?0Zr%ex5@mV=?o5X z0!=4L>2uYfr;|JS0(v@|o}5ntPbY897o{mgyqHMNF3S8<;r}$J#8FQFADZy<58kKZ59N%EU3d?HizDHYO zSsnInqym=PT?&75>PULKy+YI!*W-#o8Mq$rN^ACjZMW$WSKM|_bIRemTykIsZp#H& zE#+Wat`zYMx8;3Tn-yUdTO1xQ6#J@%*qhvc6IQoVH3PW1O&AH}UhP~>oI0nByI(g? zeO%>oo#W$0b-we~Z3mzHM;e}oB%pNJ+1;?5kHmqDeL|GN7F}tp4AwJ*I z^LccyjneIzTln^#u3}p_j;79}Q{SG>_n+&xCPKgIsOPl}QKZ$L0Dh{5_#Kdyx}e)h zY0q)lc-{Fx=!0O0#D*1I9!Cv@j2wB^&t;vk|E(Q z0EMiaePZ7C(`R-q{aypsmBidpL-rYU`ckUxECPmBS}r1B$gwRG0Ye!Qwa6DDuEeno z_f1ioPMNWMm+vIXlsJ}kb^fISevy9{R4#TCQ})CWE}q|%o9uJv4E;_b$>zg3^$XKf z|K>ls^95bRP_6FscIQc|Rd}tz*YYkxDE7#GgX(GOS5bnsuw*y!pe-8}OikA&9c#jC ziGrj?Cr~zdH9SCN)0PhxZTsb#iPi#(yRNkI$?M&Ua8NjDlhfZ zelqwK*7Xl4F<@PP`<5!K>xTsMVqL#5`8m#(J$?^xwmh?A56qTy|KFGqhs$F(vT?Y) z$J~pR|2_us z`>m_WY!KIz5bfSc=q?6L|D7Z1&I3-=1anrCCF4FCEo}0`?P&Ij*$P?SxgRZ z3GXE4&vYq6{=e^|I_CfPyyt_5=(5>F5KRlE%Vx1JH)=uIY*EJo%VvH#Hdr>R9s7i3 zv$RtOv1~RRe+$cIJ=GJ*&^6OLJBnR1zLha!P9$A6GnF(6fU;SbEZa|TJj{>qqvL^9 ztp5Z!9u~9&(edE^?Cc3}JXALiqT?aQi)R)b58_|#(D6XsF^mJpgRD#!Ivzw)P0xYj z!EygQDjq&W9NsK1MAJ1Nsw4@!b`ctal6OhZrq4tmS4umf(?zUh;$BxYPh08qciJDZ zYbPQF6ZOsneIl-V`FuJQ+e!TKlJvUa`I)jQtkf*u+DiDPH;s>dq^KZ+k?3BTM&jQ_ z^BAyVkK9m2D|TOf=^LkVMRo@g{h3$}@T7>QbKUZ+~rYPae&phml} z<0)#id-#5$hCAs+9%{ISi6240@!VU)haAuF2#$Av?Hqi12C<#4XWbFonH(pG*v=SB zcN5@%@*lm298l*t5#)ZV4`?9vlVgbFL~v-Q)uw6yhfbO?H^b5BsE|J%jWTZ!p^rps zD3gZ$&R!gJW z7IxkKAUHyW^m^AXmr5t(p54bVxxwKehRF=#f*2+<{U!t;OU_nm#4PzXhYe=Qygy!I zmb~wiBWB4H#rH8wj{1HBvSgCFE~kZI^1tSN7$&!k4=;yu`5d1omdk~Yti^Kq8SX7u zE@$Oha}&ztJTCEAEOsoU1S} zvYe!k$wwB<@H+j#nT1dX$zdukJr$49P3o2L7`<=R9&Na07d^NUuh~O|E+BGQNZt{V z%l)@Z5V8E=w=N=<#f|Ef%)D)c# zfu2{9@!ZrVdYMHnhxqiY!5Z3XY7d;Jpq$3is*mNg;KWcYr$zb;+=p`7z4MzcLfdU# zxY`5SZtLae`k~zxSHz6vHlMiCtI&?C>Ljrpr?fk$90G{nk~8E|2q3I)r{03V!Rg(? zCI}oB&08^WC=4yV1%bnsi+q@1wB8iL1cO_A@+83MrNYhELSPX=3~mIPy|u(DYk=7c zMayA=QnAwl6O=!f9WXJm-F65Qlb;>J2C%-rSxsKUUXCi!gb@t)?MB?+d&_{k8DodgukMJnzm3Xj#^*%)*=+u2996;4Y2+@6p_9ZX4@|L&Lw2IclwmpqQ>$b2%{Af{e(ypEWfz4=mS2C12*<{|8yl9roQ zRiSjcylqDoluXak{_fRs`Oua4$B~wMCA|`09F5S9qRX1H)4GgSygOaLZ zQ5Kd|w`{VXhL-ESk!)6SknmYQ&?<@tfY}(pnD9{j%=`hX@JF zLcMBFT3GDj9Xk`*N1VQQpT+C|jbvXKvm?p1T}0K^KBslM^vb2{g{oey4<#wdSE=0J zMrb%bc-YrO`@HVopX^a>APQc`8Vs|fQ@`!wKR&MOAT-*!JKOBP)5WD{5o!+2gw%-D zDgpU!O7FeZ_WgA6XnVvC@@B*U6{Ap|7-w8X9D4g)a0?AC{<31E!c^`f@!3S}5BaL} zH}yC)ev0AtD@t2=Xj@M67Hp$AG9YJzGLQk8tbYUkU((xV*F{Qyq%RvHj9mRDS*|PacZO zo#TpFRBo|*{24qVWH(b06raf_t4b=N>3lMG_8o{rdWRMGp(<^4qQDHQ(yK%U2m^;*V%1bX<9-0WhGY zEmDyIy(VSG2^i7~;~Bt^CJAG|CvR`kVzEoNSx_ui=9=*p3Ikp)K3EtSbeKSq*sj_J za}bH$j`|n~1q6e*HCRA6TDEiHC0#fuH8_rigWQy+EBn8&AS-i36TX^P+U=A8 zGn(D1r!nxA%>J>D{z@k63E$6)s6zXFs~ta+D|!Q!;j??Y)mi-PUUjZ;9zL@p&Zy#N z_TPH49zN3#b{xac^t$lDOmMrA{1s2Y?Y26yU@fTJGB#{QmD{B%u}o08CB!b{29+C? zp_u`0xD#63ESw}Qhim#~fik$$YOLsj+b-vVn`xf|49CkkOT=)L4tzlD#`PD{h~2ndDxe71jat`RbAa8L7MVtl$D_fk$nki_ zH;34cvgu2R?P%}uM()RA)i30J#9FRAp!~Lr`g2-MSX82#^69wmspZx}l^$46<|U}a z5c8{Dj_%t1m6DD6X+qLTg@NL9{?NltN_~%rURzlfv8=D3N07Ug8rjzJjzPVaFk?D+ z(q%^j^*mNr?TAJbVR&&%2y8wBn9g&+=JQge5pFuSZrh&+n@;;p`(j|x`MfL|7oFj0 z^-8cgwF@C{eSy`fLVhu>N{w&q##QNl>-r>EwMtv+c+ZYd-gza*wVS*=pQGWU`h9+;;d)c}S-Vf2QC55nUF<^`zcy zMO;sCk~`vg$co!x$mgkDx1@{c z4HQ8Y%X14SRIxm*2}AYD-5IT=<;RMN;jgPvz4AgMDWl_N5pi7REUH}Ie9CzQDwn*Y zyHMTo!h9UnE$p71^24Jv|LY8AaRj(u_HFxsqZ2dRA_I(0A_mboI$b_k*$JZ)%hA^% zFgiV7XLJ%qr}GsLaB>noU4oO7v5zNCP9}d>;^btJ=8lsS=hMa-n4Hf2m@|Z0uH3;y ztmT$}(Pnr{(mh-|s{%Ht;c8h(_dpGIvvGVQ)Nu7~e!yODth2{f==F-uI$)1izwSNu zcngCwu?PJ0#lus3+Y1N*lVjL|IWPGTTQC+0hS-AHOWwg2OhT^^wqW`mw_*!s#p{1+ zB-CIy%G$97Q+zK2TQEGg%&`TNkl%(an73!|V+$r}hXH2A<&+&}#V@CHS|A&4kzbA3 z@XsX@a*z!l5G~mV+3?Xd;wK<%KByptVRL-?iW`tE--+zNYbDx-jj0?m6CawRz52TF(7dl5 zRfLynMW&|^yi`f26Y$}QI5mzB57X<-_;TI2Qjr4>*FH1xBk*uVoGQSFD{FKJ4ai{u zN6~+Oep@P}|FNYk!>Nxr(5LTzvBaL*sIg>u5+pS!?4e5BTNsp0^7cZHCw7Z$# z#x(7&oBnu}M_L|_j|bB7$bxqT5rE6P>2(ird1Olhp4Txid5!0FJCYah9M9n+R}Gxw zrLEnE_k5*8|KIc7FqOgcJV7>ZJkN_3*2DY0()W6B-$xu<_XyAX*zLFDd7sJZfA}zk zILL0LA10ERzGsRr*+%7Te92r3|9=S$1dQ+{R9?$i4=>x_@EUcb67_(1D*P$CRluWg)zHPCvs zJQ0Mg*PH+NvGwZEZHBE^W#g0s(0WxjNx{DF{_6w^BJ#rLrp+t-Ka4@+FxStOXOd?jt2uL)Rd@F0nVtA<0le9qcs&w2oAMo^h0py3%OgLFWmV?R zUJ$G>3w9Z!aU4-EA3+i-{fCiTtXwUmqJ3xY~S+vJO~NSP51eEUnA*1ypNCiDVa;~{@v_wAfb`aP#7C;VaO#$ zKmXdbv+)-pDIvm86O~CkNnAOnxjcdz|B|`8E-jkaniJ`r71~N1Z#0vz)^;aC6n!?l z_!vq|F2Bg_AVpu(MYULrjQ=K#-WQfB<}?yBWjuMy%cqEIxef9eQjJ8^iiMq=WWx+G zmtgW&qPdZ{pHCa@`2P?C-BM>GX=3r$x!O<5;-`p&%ibX$1FDIZp|{@$$Hs}1{=Pg1 za|#KKdku-LRkH-M=GijUwVgyzZXEH)dy4qk@pv?Idne(>dyLU$?-*gq((=@su4Hg& z78fdu&l0D459g#-(}H8?yB*{q?@3~VvpV0V&)*4^-Td4Nw`PbnuhTp^?zR%=jU67k zDgGpSOalItrnV4PyV;)EovS8pDFy0_#5NO3&ki&Gb*iUCH)!tAG;bjI_H3{cQW+o; zy*1}VTH1+h1+R|s^ipE}q(Nj;W*cEXRk|{`p^un6oHi`Jt&=D@u1?DB&TON`S9R~q z*pW{hiEHL#G^7uWf5v9XGrcD`-O~E+p7~907#^S7s1rqPY(AXHakHBWX;JvX$4Xzp zxc{xzc|1%BPCeZ4);*6{f4}468KGV(RXNXWYO;!`)X)zDU+^CZ5nC8fZz8|fa!)B_F1g0c4^@Y2bi zVZQ<|os%P52{axbg-qj1=kmhpExdHf2^+Bid0eis1saf zxX}+%iof?$13*f#vAA!I&e1gL<&*VjwGcdSZ38=}jBS1>^IIFWBNU4995xqb@wPU)eV%EQ|p24FXrGh`|{5OJk2(ad07m%-KS37 zM_tdr)E+iC)vil5F#!EeP?YEixb?oWAplRkmk%Dugj4UXpeNU>N&41%1;Y*eN}Z@~ zHi56yLbo&>_^QpzKaF3t=fq9R;H#KJa2UUeBR^bFgsPRcqYY<-Id8L7{wu$<;?(QJfH&d>uNgpT(#s@E1v44P)oLL?g(U3>1?K6C~Uw4@5 zs=dg{|23C7sxp)UZF!G_YB(pfC2*?1Tp_^x7Uzo7XSVHvdcO4*5(ksfbyYZL^l`|L zIA|zpDM-SR}ONt#02wLS##&y8s2_rm+E1 zEy~LUk!qnQ^uzL=!cW4UqXDTHDl$w+#qiV}K`Mss0al!rOI{b@wEXa{D~#9F3925a zYctO`IG%gkzAT0DJh0RPr){z@;RKG`$DU~7yl%IJ3+MHvVdglm6CEWuuU}<9i7K^n zgC1W{sgYmeQN6Yh8&C)8H7(g|qM&Xw5iZ`bdWtq3TLqztZIAtZRIzP6leddYd5B2( z#&G~tZVo|xlC;W=?*A$WM1#w1mS(@PUHQ_ z8iLdAmMQT4{ZXNhma~JqJ#E7(D(V- zH~KJiBA%FIYE?0lh^f`438pwmtwwwrR>OI|kCN9g^#68z_Q(F;J;mdx&=;Jiys zcdPId6b_dP>to?CQs!v_^apPnIAVYBNoF_?6cFR|60v~zXJ30e$we0s4UU>%0rAZC zZMwkg{kNII+>U$q_E6yU=D)JRoX@Cgr6J^muQfU_Crt90#oTX;hz90DktP3uGi|Vv?~OgIgkA?*xa;Fg3N>{^fjY8Gbf#d-q$zEf+&?|?*l`K zQrCELyqU=eBJ6|wFd4Pt@MeW*RU>%z>^1@V|KZgu7_A<)9l>bzR43O3c*suvV#9~5 z)Fluft{o-D_;7g-yWqoRw1k%h9xn4wRH)=QEC*48qW_`&q)NV?}RZ4)y^URZG)OZ%L zGNQJ#_{&N&&~^@yzAGPt_j6J#(i*&<=9WBez^9rN9?~!;Gww@sEzLm-=%?_kOML<{+|Qd`o4sBg(S4k`r}I__$mrt zs)Yhue3_)L^5etwAe;$bww&uh__D1zUygOWb-bctP{&IP9>h8xOX1xysN;QRVqJtf z-XS?TtmD0yO~N`}RDLqn@itgWU>#3naTi_3BddvZDKgl{V+_51?>Sw^%ZMmKK0g<4 zk$F0e=U4sIhIsxf-Y$seS1Y`Ycz$1YF~su^sJkGZ{|&=TB;ffQK3+jQ|MC%z4}j-C z@-qqX{K-!ahytEJ*oD0eE+V%I&-XNw^hxBTL^EDM?o~gF7m$m$-0%WY*1ZoeAUl@H z;7R3)yqr!rseG_B2u~<458A^CB{i*Ik0+Q){TJ{AGqql@1MqzNi&GHKH^y(q3^+c; zDj&r0?anW-2OQsKj~^8I#f>(d8u&5;-|tD6KJxwU1af5Ct&AtE?=DB4p9-&6FYo|W z;*KK^@YYOdBuJ}0T7^(ry|-rNWEw504(8)QN%i-tp{*dPzUp=TJxHpjWS<*@^*V7h z^buIEZ^aLxi{4UE| zuDyWY{glTgh0zpZ&T4!J=jI zcd&kMYVL|)(!N(05KOvF!{a}e&cmJSuZ`mxNRkMtBoS#ysYKLCWQ!JAC6Sa+s6>5I z5@oAIg-TX7*&H)_@4ZK5W@Pt#&;5J;fakfouAlGuocliS_v;lwghlpJT#)^il>Y|= z5qmD*2fq-BcKYPq zg!SM45ge0CMo$%KbS$49%woObg0Wy6KCEncCZUo>LaRKTeMZa<1&>+WO4s>;x4t@0 zCleZnzwKIO7b1~NVat01u1mG~NG+~r!S#46dU=#_2yKQXfj zBGo&NQm;-P&1CLGGru>&Jt{-!UehhRE|w;g`%&VI+`ck| zA8u9Ju2X|_x*L1e`IX~#=dBe(RXk+5H5jk#KC`?u~PBAxOB@>j?04tfdidhuwWUfAlHy}Hid;bo?j_0}f zTmjkf6q^LXPN)63Bse0Em{%OOTl|IfKjr(7olwA+vrz=8#CJ0dkV?F_tL8|t$mvKZ z5S6}`pMqYIYksIHAu3%%Tl)!7DM5gPj}Vm_=c&ukOro0E0jXph&SX!JN)y6gpxLCJ z++swMP#MEXkc6@uS6d)SsM+(?kc3hmHJ2rr?tEdwnL`Y?-!;5Pf&1MrZvdQc zp`104ggUD51~}n9ihCdlwTU{b{E-}`br%HTC~a@LK1`0%z48ihlp3f>bCaW#<>A_7 za+Fpmzfd4Y>D0498CKM@xn=}O-Y z6ur)t_yUY;kNl^2ev0yYh%kV6#|S3EfCTPJULqj)+w}+p1YIWMe~EB_&FtuNA{+>w zJR(Pg1=cf`AepXwJG+Az5DI-Z17OfyrOt-%1XC>^08j9>Y{Cn|6MQWwP zh(Tdp{wZQmpzuNj`(r{RY`UZXL_&!lB@%>a7qXg~m@XmgCizya~r?i9pwQ!eYTCC-PVZBY=zl^MPfN@Bc8uG(k1 z_ERZXs7eanWGt9v6Um!Q-F_o@g5jO!hbI_E7Nv*vhBQaq_sh*utu089Hxu4;4j&$Z zH=UNJYVd^fmpuZWaK3&ChBqKx4GDMyVvB2pV6#_W9R!;L-0L979I&?D`36NxGIP3q zgqp05XAdNq8!pO2?UvDT*qqdE<))&s@2p=!fnV$&P^Y(GOMyQo=>m7V83%B08iVkq=bu^e=K?(v+_4#n?rOW&dw_f{+ zX}I-j-1moDudbUY+E!p^9Wf%!lW4-bHyph<^!T1~(8bA-tcnrKuGqHx-p_{VT2N27s zT&Jxz&2X?XVT1&SsQ@c_Jz8-1XF)3DhFfXmX~+$y`9r1Ru+c7PRAg&!g52<#%KeZV zcD!&Ia>JjF&iIf@hSC*NsB}={HbHAir^8URkgrUr6A6SH9H+PbjOp$j8|ZbZM&F$O zy-;brihariUp(f@My?SKryJQ4k-O(pF6MvLh^5|or(u%p@kqNCBMeZgRQmYwc?Sxv5kYCtm!i@G4GyrDw*v1`l zgc04@bG3pnq9-q=w-ZKmcaXMWY_EB`6xjy%@HmxyZ{6=y)92?x@Ni2uge6rTz z+E4tp=D!@z$BaJ60Syb%K@O-+Q514L^~u(d>+$rrfLzdog%IR|wr5B{E~r4&7;-`X zN`f3rQj3!%FiADe#lz^ud(zf{kWk`nf5PbYnbQGAxA14X zDVW8Wd*cC(5@)yov)raUy=gMb#TTrBS?)*#C+` zwL$Q^j#wcG({I3y^~lz#^sl429uCyD7 z!MVbD&kN3#cDXVb$N$N$<|D}GV(LN#%e9HU+te1Qo`d5s??xXmCKd!`*X|QwEYpxFRAaPUh{J92c>I=$M z?sg|H5)wh_pvCSNWW7KhB&vk!;X#6RKE97UNTkKq!h^(R%Z&BpLBdFe=QGi4cVBTg zAP*8q%|3%XNcgMyxRTbZ!Iu_jy;_Bp<&oBFfSCely|=l6QXguoY0T7KJ4H*Z=!&MjA z|HR}|Lnn#jp{VmG;P*@>Wvs-tlbv-MQegP5)7eQAj4W^!+V zn6BBj|5WYwHerbA-f&!mIib$Wh+0bqg(gon7%U2-%tgpx@s~NomkcI@d5>W*u{HVt zgT|CZzy>mCB=SbVpt2}_e1Z%rOS+7=$e_ZC4@8qetK0f}9~rd{T`{jD zqn6`|);(lYEC0@HL`F3wya=;hZ|k%SneCqb>Vw&^{KS^yWHvl;z0Y5fHtKEId`p6i zdJpEe& z?bhqqO{Uwtn4K`JsxKJAusYsF57Tac%Vn5$ohA3a#)!HDSFo`OtVU zF&K>_jfW)F{GFunu(qBRNG6Zu8G&RHIGzuvR%It=Aejsme+1TRSFphZVZBxcwOk?S z*8R8Q0A#{`{u zDvz=t#F9zJQ%X+KxyZM0lDnj?%i9_HdM5o9o;_td~!<9|L4M1O)(;nsY}dK&d|`2__M! z^-l926~wZ6Y)2YEsx@^w08*WkL0wC&3sDB|fq*>q`))SEVFe(>r++x2EXkw&9H z|3hdrKB?}5f}+W@{ZLRWtgC^B<9QmGM7FsHnIuQllZGSTP`OR8HVsxs=M3exwmY6s zZj+Mnhc=vC_;qN*nNSZdK%MT@T_LE`tyr>#dYpIoML?~EJM%)luGpR*>UHR42Gr}! zdR3ub=d*YYVD!#MLns2Hf7Vb4@cJ%6Z-CdwcN_)!eXUdfeL}zQiV6al{pu^h0JEpQ zKNTM(IQ*a2TQuEm>rj%4IneOe<(JkF8h)GOe|Ng6!fd$Hb?y5Ice-A|^KhqgQOtom z-DR_TK*!_yA^=yq8RP%1bgI_};Yv5?Y6w@lQ~&(l~q+zOm+zI-ia}sQ!B-tTYk+Hub)7VkJ1mMRK}r= zOo>ic-;W`=TOIkbhthG5rLn3`>ja9~c|bzG)gP@--w%OCsj24>Xk3t=fI#DBrtP#q zBQ5`{b4wZ$jlRNrflzq0cPAtoS6(!SMB@o@1xPf;>i?H$q}ObRMB}bPHApmu7Ov6h zI^B)*?4`|?k4EBv#ghZI`x?;eW23XzU*zFp4Pz?@=Uim>CDU<~y$r{Ng%;$!??&7V z!#0noPc*fP^-SHIKm$rTFK(+;SBYQXNLQPeJ?Og{`{)*fw^Q;CRNxc~esA|mhPH;&O+SBMQKJ%O58a_AaRGgEnr zfwI{YTSPq~qR}M#7~pj7V|{Uqa612PTrwk~(KRX!AR3ijIsx2HGdF(VcKVoz1GaNB zHF1exJ5L?muS>w9#_j!j1S}dl^el;hNUfa)SO|#pnV%kjO!Hd|8VSgh?`$T3Nh8Xr z_lF6Y)JATz2qBv~dCvmbG83QXE9jQTB;W&sqf)x%m9{wD5IQQOvg&j`7V*^OJj)$^A zY<73@BiP|Q4^x1Y>~ohNnCx(-j)}nzN8m~Q9EsVQuzi5%`OS{E;CUWTZH4D}lW)WD z94}F32hZ_&Pq^SYz7Ss~&-)Y>&K-pJ{WAiK&|okZ>kuRjhF7;6AP=DP?gZokB-ysY z&$L$aApA^Ii;lw2a{KUc2pX3zN5IdLZE6+#EOTGY!0&wJl_lVaUb5ncpLMh$6a1_v zuFKNS1!@$B7i@xqLG%C@M5nbCZbNiBqf;EB(hoXxAu4UIDGp-snvH>wlwP-R7^2r6 zmT3^Z?%0z7QR{u5cp++S|Lqre#ZSMWgQzyQ;wp0jpW8^c2jFv;E04$jrnqQswxfSi z32ZLqU(b%0hf4ALR1Sd6#cnAFdY$&moj|Xfp0EH|U4zPRfYp6xT^CDecPtiazQ0X} zF_-0=QL^jp)A|CtURB{=_sFhy>9ZN^dM_W7`Ab&4Yg-yK$*R}x zlVJ?xvR22ZVAp#@GjKQA_3Be}Ag7f%5*I+aV0S|UBS;ra-g6x@>4HVEghLmMeLPH@ zbiv+_qRv zGH6T^kj6XOAyK)#bQTho{zA7QK`GH31PMxNXZ}BkU~<@qA0iRV>nx+INv3n4#uqZ3 z;TO+9hI8)O2FP&sZhHk8&J!QDj*<*#pDPDsLItnYX_HK-ZPubW$$T~80O07>-!;u>S@utnS@ZQ4)D5>5GvEH1W2`( z4PAp&>r>-ONU=5)=R%5=pPxUPq+q{s^*{>t`1@WlE?TPgfI8h2N!98-`w1!9O^xx8 zqBXzt2~xBbKT~*QGAP<(vh)tfj=$mVZ8&;@l-Es)Dt@1NcBJoyGtSBa(f3fJf*knMTDjoD^p@lXnGsu zeuC&EK%z{JijW<^TPHdHlf$w{gXu((I~zQ}+N`r9#D2O|B761jxALId0kd_x6YnGLV#D5M`KqwIS z%Lask_@_P*kIQ-yDP9p&a`Rf!1BpkZjc_HCY-?|GD z4sD0j;H+k~VTH4r_Q(ZX(3Smza8wsuvf(2~^><@tII2J8N5D}X5Vr-6>WDSza8&pE zU^uFC`SYU5QGL)(8jfngb`AiY(o??9Ksq(ZPXV&2LtHkHO`RorXtI}D{O#y!Ae(M> zP56(L%BKLNRD*&4$fygb1<0tmTle=8k=etpS`eA_hWp-qMpuLP#aII^_f+BEK|;sf z@SqatxV-uc5rLJxh^nx=0(e|sp3*%8x69)57I3?qsq~True*43VAGrW4&v(cl0V7RjE^aA9fv zc9C3ID12pJ4{j_;28ZFsa#cPNYBjU7!3Hchf1@qN)80`uqG|EXkMetNO`y7yebZbr zm8kag!LZfFe^8GE-O`+D0#aY^cuGQj0x^&27u*mmLCV?YGpXnQAQ@vdKCcy_=tr36 zszYTH=;h(<%wJ_*W5qj*v`s5DjP)ZlVAb03xe->YU#I$EwJNwV9#*Sn;`*>!Jy#I| ztJ=i_zW=Ri**5;Snq4%6)vWUWqgIV~G9YS|Y_H;=9y@{B&$kF@1bxDd&XNZz2S(5v z`my^>M+-62nyo_jsCNVCPKfRA4gFR4=;n>%-H9W#xcmkm`QsIMM;O!nrPcwor?)zE z14}(VU0c}saCbjiqs4D7(B6y}m<|A}=JqZ=3t~7t`|T;fY6=1#0<301%pZh?5BF(< z&~PAg6ks)6Mdbji$=<&WQ$&gA5-b3;np?+mfmUPu()}t?D$3aM%I?qWLb~iL7Wrpt zFaxIt>*W1vw0%tK;mC(toM%x3PPd6H=a zQj;>c#PP8V*9q7AjCBN|jN?Bg*RT44y(OqSpXLmckjVk=LbC%|m~pS5mr!H`azB%M zzB8{HGhG*|;(UTp^6K*}ZKuldxr275t}}(`pn?=nQ*j+$KC})zpP}U?dx_^$dDjZ? zdRlW|$|71%#pzwGMC)lj;5^aEd_ECoRf8kU<3Vo)d5w;&ch zx^4u-qKRDgK(FcDpAGby<))br*Uxq0b;=DuyBSnn0$R?6hj)OMWBVx@=r>1Seg*o? z7{gAW=O}k`0zJpkm=WkXA42nho6BEJ;>Vxa}5yBq0{*FkXddb=01B5Umz_kynNDWiYya2-Jcv=fYXg;&UO;yQpNp?@Dh5#)9m$jOw0eW z=f%TQs+2%0Jf${-#=%o6H692ECQ+9__*H!R%K!`~jrIB<8r>DJ2}GkOhZ;dNy4$@3 zm{I-gn}7lJZ}c?$suEA=z|Ye3{tNh7inqOkpJfW`fS4dfgGj}0C;$jmYk)}=flxVg zUtU9~RVf#K1GQ=s#Rk+XzSiA9y*l%BfbI@Y8-8N74k%VrPivzI1xt9*8Yo!B-I74T zx;x&&Lhr;1?Ov}4{N%}j}wZ-9XC-r9!0JD*@5+*BqWnP-CnPRal#$i3qVN$^;Im!Bk zR4@XLe}xLhpB}jxQo&eZD#$@980o#L^`wHa)mns7v23ON{4QATJxHry3>+|l3P$r^ zc7IaA7-YLH|PVsqB;iGRljm zhXAdzBwqz+mF>4o0If2wb_38V!r?N2W?3pzWFlymmiS^oxyX+n0F=vEi4d@0w9m9e z5*Cd5*Kt6*P{;IM+7Y&k(eGyfhY><201m@ncVm*UVW^tdQ-lq}nkWiznUF8a2MJt; zyV*yVz-Y2X_W_KC(a+YJz-%^5aXcb0nT9VIaZrBus4s zypE7Kiw?o-)cKE}Ab6f%m(2jr^HEL;@H{n}s{qe~BHjSrhdyE>;C;qB=rlNPQ-2ZX zwzD1t2qb)cI{<~&I=g|@+%#1#c|&*1XtWGx*%RE>hvfI6P=53xX^%& z=y&xHID8b^3~>0~?FPVx?@cHGHoQcF1+d|>u8sg3-p!OB*zkGR#efaJSTzr9_~ys7 zk3h)~PW)p5Z1|K>M_|Jr&uw!dz2JKOKhO(SVr-%sNHMtS#w-+r!|u(& zM2ts%OBL`q?fEDLj7cei_uz5b?I!gGUZ;PXwZrSQGg1SEQp8dad7Z}da!NMjahh-Q z06b3T&c^)rIo3^s&#|8F1bmJ;eip*#SpLC^)dXIryUq$e$2$F=;d7jG`5AnUU(e9N z=lE<63w(|pDBNvBXm*P;PVha>eQF2p*TEfW)x`a3+~5eZ*z7B#8+X%Ou`O@D+B^78 zpw+2j^CyuLy3q6!+_A2&ZNMFS?C98H;O#MFxJ|N`$Y<%BD?vV+NG*VTHu_#RWI!?) z*>{r+2<7+77}D!XcOOFrq%+Fg zC==~%2@Mi_(1}f)m-hJW>qeZ<^mKj+(|EvRhuPI=Hr&dB&iX{|PW<vZ>);?f|EwnqKSyaW3}p$l&lV%@Wu^9BqI$v zAR_6H9`)IEyaCv@%!)=VFD+%;nih>N`+f^|QfZjNjbvD*qFUq(;E_eWOnm#_yL5<1l`oa5(|v zcXxd-h)HUCwj3rN@B!`tFq@n&Ux4v@;EOM0K2KFXg3RZBt4PRvs(tDMc}`)_LmNHBTc=YA+rty@O>qzBW$W%w;8n9oP5rOFAotm~^SfXn7Lhm;f3 z`M!)b#B`4OXC>beaGA~50{|}joLRCIO3}z=!4v=P(%tMvbQW!g2)RtzQXEw4nQzXz z5yhhL_qCu{r0$tb62+oQ+AmNnp8B27K@^J$2mdN}>}*G=cVB>Fah#8CmM9jz(=20X zibWKvkUpE8)`akz>tI-P-=GAB#cU}DH)`|sOk6){z54u-Tx9F9PZ3$y;DZWY%gR(F zviz*d8StzFzk8pYd$!08ZIs}uVr{C!T!W5pu5bE;6-Rwo4+M8%&TlPMVyUsX#7Om) zZ)gwZt2L9n}qXcVnda$QZs|r~%3bp~90CI2*pq2D~QD zhNNBX;DBI0=>ZN1_FX#Qd+eeN|0C_Y+>C*Bo_Resdr$lFq-xTYsIj`Q4wOj_#Djv z){6;uL(pi1@G#xd1BnMA{}%edZzXgE$f z?f~t^wAW?OZghmHgSO*nSwCnyBB*Q9?L-99$D6!FJRlkd8^QZgoTUxk53wkH@P06z zyU$9zADewUK_ueB`*VzlM0g%lf;S}OG*=q&hCJBO58{!Crs5?tcM9 zWTNb31?H3Zy5;?{ zj&Zou=s$Hz^x;#^#GJx$#P9gRzcF+{Nu2KcR28P5ecgMwVGQk4>WXxK(SZX_Ei-l* z{YC>D6))Vd>BGMp5dG$<36zqK9~E9~pztGZUoI8nG4w6c&BBMR6Q`icN|it3sJ2wW zrcbIK7qu)2(tz9O6UzvAzx|#3!TbICk^p$W3*$<6Xim2wuJe_0&mu>0(eBsa{cg+m z2Jd&mA5HLnuV+dE@As9O9-8-i2z7WrrD?yZLCkTdvy$+7J#_Ve*QO0HW|r3LdAOzlKdH3=g6Wbn4oj zGd1Yax%%H}KgWhv~6BB4^>%A4jmm_fNjQU5WfDyFrV_AmD+5}X- zmzm`f!#H9Pk!u`_jK}roe}g5judy~=1QVzAAH(fX| zpY_@4k|M+tT!+<}dT`u{_osb0lhLkZjc1})dT{R^ZC)Y61Vrui438{1--{>mw$=Wb z|BX0joFcp5rr^?UnZ{MjL&%!@UW##F6cRcyY?Tt&j*iTJ&~zFoL{}%-`2@y)p&-G? zOULxmQK4Cf#_`e~Z z^Nc7Tb5I(EE)4W_v;1kpwRdd0Fil*q{sR^=`Hi z5UNIMtv2kD9c`GU`F540Y6DvS{V7s|z7tR17g@?qt4DWd(hj8R*W=1N!XZnmyO55Z z`@6gAt7&?Ub3P7SG*v_Wg^U)N5}aQybziHEmjAh)9^kF8z80IjD!k}z+m9Oa+J?RE zm*Tq7m@Ua>KhemEjz~tHV$3?F24S?C^p)U@+}3;s!f2tq6%a;a7Z4jGlFBDfpF_Ml1)PK-jMj5~XsZuJOQU66eF15-zpEMnqw&~F7BCu_f%nx2 zMngE6Yw67SdTb+q1NeSxR89a!!#cVKFd94dm;gm%otDQpLeVG^)&z>iv*}!*XtaNP z4-^gE)jF)CF(Ksdxs!-U;@fXmP{c%%=Q5j7N8@~)-4_iVjj&&D_jY_Q#48j;p>nZz zNg65_tBatsfrM5wLS;joJ_$r52biCMh@>s$6|CRWLQ?X``dyt(64v#p8thj}i$if+ z{c2eE&u@GP>waH5J_`2tx<-|I1NVGERXu&M|JNH^5Bq!p?{(oeIT)wm%u{1Lu={#Bd&5i*(L8uCXR!go4`{1M+&U^q+uC=&vo!XKp?>p}RV zw%LCK{-|HoP-gH~J@Lj_i~LpBKF-=iST84rkAYURo;Q}RgT{LKh)_tJ{*yBi3T;cEOVq(0cyx+wupq1%^)hh>yz0!yMx1Z=GK`d>^0x>7q|`Ly zN9Fg5l7wRsB{sRt7qIk==5)V2{q1rs8v3|;wo0P}TVJ@LF@7rt{dxZJyM=xNZdH3K zxJ|AUy(9SlO04}PyamwWc0&kq3?){ zKKH!^$n0ZHXNb(6!m2x+z-Axsb^qL@=>l3><#v;G|0rHDQ@;MvFs-*U@JQ5zWgOQl zrzOaJtwJs~{U!9mzi9nH#ip=(jcDMqr3U-VI2JP!!M--MgriUS^kBv~W|x1LdNi~Q zxeS~8>9gvGLLQtv`L78$mw=5}M*F|o7E6Ya=oM%7}moaPPt{%N~~5)IAzSfk`hR1fw! z`HIWnZX4Q4@!&%jT9C(JN&l8pX6iE_lmo$t~3 z+h`&$ZRqK>Zgjim(?I<|3#zFt@Y?Wu6eS(-Yi+J-K#j)7^7cIGMLJD<%0E>)5aV|% zYvFt2h?|COvZAF~-b;LxRiv9lT0RVd+TN8o0Eg;MivB@+Crf*44pq}m&V#nDTPBgz z7A?B#Y;kziXX#M+!YPFB`ah)hFlJ#n?q0m}&@{4X+?wGP6NmDxYc&(xyAiWbyyX>% z4*c-++CTSYhR}}Lg&}0qi%lk2=BSuo=yr?JJ1v)fY@W}}oH{ay3Oc|1zQ)*$`Sx#! zesyXDb?Xjp_%zdrE6#U$4rq-a8?mBucC_u-+ppKADrl0++nh}lSS~I2s&vfe%2APh z5m+uAOf*AFCB^H(j)%?SS~LYSu#ATYeD-$)xdJO zUtmkA`u0vtznlXe@IwEi065HKxeI{9a*=WHjz2w383N&uQQZ+l;<%e12!|u_2cHuW zxshANT_Pg?QpjmaMCAW;4uFXK-xLQ_wT{?2KvnCba3fT;Y>ur22Ls zPIuvvhhFyctjs_sPCfj1-=G^y+XW~F?W;p=bt6J~G`m&LjhMyM z$8pGFd;IKb`yMPJt2I)Ul7miqnM<&1_F#TVb9nK=x=C7*H;o5zAz5OL}?S8<$u#Q}#nN07&H9F^U4qU@uEL*0K zYq(d(DY%BGjf%|urd`9^gcaXpTh}3Dl`wXL@J77&K<=TidJhV*(aY+QsmID8s)wl` znLp9xxz1K))^gmqKJkc;MGLz7K{003tp)d<{dSmXv=3Ez(RUg@Xuum-L%)0+qg_G| zIJ2DSZ^OF!KhBkE7GS}a`%ukZU*aA`s@d^hzd$wH>v;Vzsb1L~0c7tLm)mvPf0Ef6TUjlH5(bEqAL+(wu=&L?TI`K+nn zgH>vvW59N@N_{QG4y#iG<^!-g4fi}NO*Vf#v#GBaEq*)s$Adiv{oEz$o)X)Q@}xG_ zR53)O%-D`R*GIjm^k}f9YH=76F-Xknf7gwU1#9N6uMI$27Mxv@Otk!;c>Y$=5B`3b zGhnJ~)m#&bP2{CK9inn zSt$ybtL*TfufsVKiaShu^N`t*!^@vvwPL-g*Dm?*;}BOTvNQP8i&sBx(^TF09C0lM z{wP2H3)kQC=-#JHg`w>{c8u?IM(~|OJ2yG><)B78^SX_F!#MGA&xB}~1HSNdkFzRm zm^2&yCYU#vfJTDeWEftW!0*e}`ZD|YqbJ7a?#r@{;!7Sjp@(SyAb!5OQ^jq+u`Byn zrod!9T4~ICSN2^${%Nfg>zmbvUaVX@_io!L9{(=%WjDoHhmIAzh~Jwth^t10Pgj+- zAnh;z_zqPMVu>X7jUtiFNIW&q*08MuZxB?TQnu+t^w)=W+U}>Bk3$3&>C!sU)eWcH z2BgbSiNVj*qL~)7V?}%kGb@dV%JwR@N~#Nq#D;AzEw4hlmRutfdF|+&>8`Fonu0aO z*4UG>{YwLnTvAT$f836SLLQBcudYY2R+x)tU9Se*q9FGDhi!)^k%o}V4~OAwsD-T4a3jv0kbXyNTVPzeuCr zAJGmGO8Dc2W!u)g3wY3n^iw8YfBaH~-#d7f*8BZJ4~;bVD!2c@GdD-d>OQ~63+DzF zbZ8}Xk@bvjbm7j}{L`z2W*Q6D%Kffo>IpyW>LR~Qy0#tneYAZywId5%EToRCxFw#4 zMbjlTOdjVU+h?nK!gsaeGVL1K14a!fA!T~qi+9cV@9$WUM(OkY0te)oJ-5IaC23K; zgE$}aH$MPp)b-q|v&0$2+pG=FsB?E?!5NiI&j`+_FYe*sjM{#0)q3Kb6rjpyrHOP> z=?OQG%RXFV0dmo3s4azJ;C>VKWdQe^yxr0%TGNU`lR+)uep53huWWgA+>Rb8B~W9zM51SJ#b_&#Q@;J$zo-HE;bTpVzYs{P1~w*;5Ul>*r3v z@VWMDeXvA6&rh_H;PYH4c-QZNJQa&VcCCT$dvF!~Bl5Ir9;vuRhW51Dz5nPEc}kT% zBLPpT$97nKk+U5@+S>+oFZ^miZ11nY(`?)deR!J1G8ym`+sd{Mo??GU9sTbqcV!$r z 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

N9a}yp*FL!Cq+D2A+5-u~wVx6jXkaq=*;eT} z4f;0;?#xst2hc3LYOr!}1hzyHU*vO#pz72+3GZ<_xa*ICPC4E+sK5r7>ta)uZ-0dJ2xG{(QxhG787{92W^cHwGsU zb1;`Gj)BBb{+{ukA&7AAnS7Et0uHb0toA>o!HB{6!mXS%_;f&asCo|r&Z+(H^291(_k>P3a=nkRiL>beQ=S__ozXp@{OC%!^Z9S^*60rt zJ6|S**GfK~_RJxV%Dgf$eYY1rY6u5hLk^Vit|TS>jgbR*z_M!_e`V`E@J_vL7jBY%9H!Bd%lR$W=k^@l~JB-ul=B zyM)uLSQq!fi>rbA+$PByFV%Q>Or!wx8tcgZ`YO243aGKv-{I5}9RP2}U1Q6NWAIAb9YV zoxgh@{C+p);~v`&XG={!Zfoy{+p|$-4AFXUT_d%9TWl+&e3*Q(nCz6)Dixe#2EKuW z%)v+D?G50{IJHW-wh;KnS5^0Tmw}5&rdV5HJrt*3zGJ@eJ6u?B*>RG+864=TVns(= zNSx}3;MKv>8q&+__P;SrK0XDnN5=|v!G?JYv9mT? zN)M6G@#ATI-66x!)v6m8Mq2NThn>#Tr9;p%_xtG4m?3fzpL-c>$pFO|4r^z(VOXE? z-j?$z4bnKh9fDct@aOlUs%5t1H_m+~9hXV;z{M54ry|Pxfn8rhnyIfJLbu&iHZJai z2-|yGrrHK!bR=x$WpWRnjqz%(NbH5yk4*t9N}Iv7z(iN8ryVB5CPsbMS3&_@&CS)N z06Ol8sB-aV!{DY}L3VDW5f#8E>$}aIdysr;_2~@UlQ*NoEj!ir{%Z0S$1{C0zd8)RO6b3~ zUL1un_eXx^r-$IAL*BC_*D287c18WvvPsA&khw7)O$r%jdD9Bp$$f?W%&%b2F%a3^ z)jyRs0WYr1Dsx$nfVWeCXp{6Hm=p{WFKagpL-sXw?u^hj(21y;v3uDJn;u&1<5TPc z_dC{{+_DvLJioA8`&%Q7Fv)VBzEcb5J@m9I9NS?--|Z!M zQ5`TPCZF~^um^gJxC6|VcEPEJm1@=^T`(N@zQ9(t2R>9b@4Ixf1Jde=3#)8Q2Z3(6 zKInLKKTMvPalYKq18ia`zsA_fsUs$|SLQ+th>ta0@I;saPtR7K4cbg89mHLUiV+an zV-U)lFa~<3s;z{~XW$7VfKw`A9EL9w22+ct;gC<9hakC69Cbci*62O~a$|$ClUHXT z$~vLU^Wiw;5)0OcFMKuzMnB&)7;8>J>M5mrtQB-HaM@tLHDw6s^$EI$k>p-2^r|C? zsU7q$pG*t$>4z6QNpb0y`=Cu=^!odomEg~P|D>XO8*HLK>epP<2%kWWN+kV2eODn!n@4e8jEPU@5Fbm*&e?jBE zMmeceT=f{%diI38zpFl`ni8%LeTodb*gKmwz6Eq8gd!xQOyhRGe5sB61{tx+Ul=`vDG3Nd<>X;^DXb6fUmQVv4x9HrQ6-+FBcL zFrM#O1V0SYTh{6LE6NpRvk!!a9KVYYi^O_5c}NjO@i1aWO8MHI39=F#u^vDjz>+^g)flt8N|^vY)sY z%Rwzt^Jx)WqybkC;UZmRGx3~{?2^y=vVQwXRQE%^=FXFABFR@i!GWt+_Ud6=y@Gw4 zaP?{y9ZZor@(b?Etx41~ZUxylH*foVZwEo9Ox~u>BB)p!m#j0<4BO;xyK0;U8fW_}!!PMOrSmpV>>&@a;7~(%uz9g|5zOV!Z5=Vo`SH}6tqj{?*&K5LyBJ{9t-0Yf88lJi`F;LD3=K%|`*wBy5Gc25GuDVP z;Qf~8k|OL3u)HtR6aJb3nL}DP%jM``eJ1S`a|0cUvU6&!$q_rQ?-Z9V{5$~R!atQ+ zPSAj@;}3CSjS89P_{*OxLyk9jsz77Ovp=EcUMTjmH~0&-mM47N~cpv z@Ujd`KfLk&DP6Ro4;G$#A$Q_rE0_stDXs{w0dFtA9Em`3ks8c7>g=r`A;Q05R$J?! z*6;GyvrQfFa74l9yjmB;K*p$l9}RkaiT7Gx$g;WT!a=?h)k9$OK4bG8*&!e&0VoSS zCx-yKKkQ*A8;7D}{iWZm+rUgTa(P#Ye5MJ=hE@pL2r(wp8I`M^r^(<-@H13M(8< zf(I*{@|}0F!Wj{Hft8H=ZDp)vHdk-MN~SSX5et^q9ZQ6$f@Qm8IuxS;;cB~JC_(iZjc)3&bB~>3ZaxTLw!SiV~UI~+g zdw3-Zb?)S(4Ym_t{w?)nuHXZ=3iyjaoA;Uv2+P3HrFKc=`EF?h4L*<&Hc^O!1{#SI_$+X zv}SV8^cu`S`0|;hTS>9HF(7+`llC}pnM(B1b`awrcTwitS-&aJzSqRTR7Zp8-vy#6 zA4!^#&DOt#V+3AT#65`F&;rXZ3lLAq=Y4jik#5gOH;CRpCskTj3^fUSPgT+j;Z0-C z?YyfsaOavZR!|zju~&vr0b*529jWRO(TwjU|A8z7&4%doLI{W26Ao6Rc|v9U27)ObJ;+MnR|FTW!3yH=!b3*a;PZTY^uph<~~sB0V&Z9*gB0rWMJ#$eX%a@JlQ#|caPRj zu^Ir&J$d)-mXcDeaftk;Hv^y=l8YUc>iInEs7Cb9V@I`A+02{TZ~7|(ala|2{2l)M`!x-0%I}K$9Hc|!kiy~EmH{|ve)!Xn@*tc`4wmvK z#U>|r4}92IbuxeZNj_`1;%)F*!*{~oJJgQU|LQ)|Viy+Epey_A`x+w}1iHT8bD10> zbc0^VeCAy+9JB^&`ye{?GnG%ute{t0URH?rKWZy6_ujG|j|h9`%CZH@62D zBFV!8oKu_lfA_|LXp1>Q}g0q5HVD?PV-VTL=ENS|M6>wCtnxJ^eENB zI4r`NZo$G)tm(E1(XpnR<@G?7%S`(_s$5MKLNb)d#U$Ncl)B4x6sOFj15kw0u=|W6lrcR6MJTI4W)z#E zIX-I^*S-cX(jY)VV61J(~l~`_Ec9mOe0NmtK5?Yf6L0HD{Swd$AS>>8{ zkFOqutXTKN9M4fuNb_z^KgoatURHm1Y?y*0teR%T0+wkw-(Il$WXCv^H@HZ*>rR8! z)wlQ4_sqh+Zgb%xn1*hPRWY95$6+qfe`y8j@5y}hxX-UJ1!Z@HA7ybeV9DCvW7dYF za9=Y>DRgKA(wjeBzuVgenhRcieC5~&Yx7JlT%GF!6N`LyIY$zuxoKy!E4>z!%SDKw z@2eWYN}Lnrvhau2D3|e^;y0w^GS9Hv`|c{Vfu54(W5cZ9FtmN;!DFXdp(nWkwc)U- zV$_B&T@gZUxcA1+(I43ZaA{#PGuNSBC|u%o^J!ThY_N|&&G@qYW7Lf6pO>O$Y&=PX zYV#(xf}+qVT}`<@aNH8ecj+y;$acw~n0)@7C5p*j-1B1ckMo=NhFts#s!nAuPOYnk zxXU-=pB=4)+t-ev$h;&d4n=0E45xXKxjr}hmU}szSrfC=ZGSd!9X+X_e!dn&cUqC+ zb1yUzEB2%K9Jgr<#pi|mQf#T~$?h-GE3II>5rPK_(;G6nVK7P^Md{oSTNI^DrH`X1 zZIy9{?cw1-{=#4% z1k+G9wT*sb(5Z1ZfAG;L{PN$S8e>g^JoYWayC=pVb|t6Ox5hDGnrt|cY)l6Z5&1k` zzA>n>8vU6WJ4OJ}`@YG(j}FtH3{PpxkHD}|x}Cq`D8w*$j+f2RA^Nz}uD!hs@N;J} zwB{ZLm(il&9#2x&*Pe^a86|5tEj!V#qz`cFuwAC59XWs|NmwCZ&mepsIA6VVD_Ju} zHsdowd_@O7BU&>L<1^ynOQTHwK4O?WxAp2oYse@JIXgVjk|le*@9_*jWzxk`R?|?P z?t}9N4HY-s8%uWo}0p&^~;j!Qv<#n0#OAS*Mae@P6)RHdVclU^La2NmTR#*BbSG{KM^F zxGCU8;L28T;jz)V#@`F8L$uZ`de#bKPfk#!5@>#^z)GpEHViAJx3Ub>2MgpMpgyRQ zS>iGJD<79a|=xfbQ6%WpbI6 zJI3+!KDuD1m4BiO=1Aj27i=d@o!Ccdw--KqG^4cJQoVZ(ly<9Wyb{amS#92TR9Ssf zi-u)&j@((iLg(HZ;1ycrx)ZO^w3OKDa5);hwyj>h`r0sQh~LTLYHnmX_;VNd&(}8|63)-XAE(nGcjrn=TrQY0aW&EM{N&oyHZ|1@SkQ6{aiq!x!aUtK^>d z0aN2nG^dZA=|Xe5sYC(I>5ZPJQjZt>hV$7%&g@qjpshbY`t#KmAf!D6(Y3alZbsKS zVWkne*7o^N6Lc=t!adql=gaN6P!`RW61BbxYVT*Dn|($h7v1be%_MZQLt_<<9;kQ2 zrSE>mQAKrN#I`H!E;*F^=<;3C>7>cMUS>6#+!EDy(d4#|6hF;0JV?M7g*{R;q%Z%# zC9lcQrXNn)C7}B)_2UG(-`gwr(f#hd<%ajS*b`&Czl(g&;Qd{*U@aOLtvUv1V6=L8 zpn;)!CIXvI0j6KVRMRPaEEk*3xqY7m{ML;_X^-tL^{Zt4pA+OiWRWleEBG~PUaXpe z{r9y$ZRMSXS)+-rXJ00uU8-91+mk7H{5$BJ*4!M(A4<^JnJ@*_ELZt5TPA?E#g=2E z_AGEXZIM4f@S|?*1-vc<5f{%Zx@ET(zC8ylvA9x&}sZyGelIhNzf1 zy{ru)hwW8}O41;dA1&g}Sxf!^bsu7vu=YYa`}irhA9Q&Ab$?U+5FNG_iR;&|WQ112FXHx!wyBzxm?Aa(T_YK5(lM923p$1It{O{o9_k1JBbKEKD5R z+psX%B`A%BNs{GzRNaV;js(hXa{8-WDA|qcX&hRqZj1Jzl{)yG2d&h=io1x3Go@HBRi0i`M^?wl#KD4T}a97@tLP#p0af#4Rijm7}7A!@e8g|)QiS_S%hAM zq6t%^US#Vlk#5;*wFc>yWvLQKw~P-rqB&h-*nsBrim`k&r;|hP4O5cqrXYPIN^%{^ z(x0Ow*Y=0IL#QG)nCUwfu{`f?ViBt!CW}{y{-|=4xtF5neXAoR7>VxJE|fxV`r zG}j269$T4_G)aSuKTlI#)keY3YbARD_XO;?H@G~0(+EU~jJ))N5lA_E#ywbT^Rp^0@ zAx4ODm7P9Cp<+R85`pDvTu>H)BjMv^C6J)&3 zTx~_hYs0ctUn&w?AnRGA{QX@G;5T(maOvV6h|+(H?AOkraAd#sRGT9El?o*W3j6y> z4(NM`cSR=@TK4Q43?=9gzU2o(VWk^*5elmnw?QUs4NElMmLA?~@wUtkiNo8n@c9Zv zDC}bc5usSZz6J>jmiqbD%osYbHPgLx9b2;j_@a)H>Bslr%rPFsp)ChRGBhy z&1w#&Ti&s8C^6~j+MTYF@}7jQtdf1gBpH>q!8tskCF^uI;+9Nv&BV5R z55pPT@}^WiY|EWQMvmXB9|f-cUwlAx6yED-KHn!mR!~Vtg1f5v;ZAyOT23&TcG|r8 zv^d#<$-mnA^NmwKy!+wJd-)Cx&g~W?9+%&z!CkIbl9u&!@G6VwA0e5Ko4iduf)jKQ zGCdAkRY{iP4_&^>4jE$29m5A3CND{=^(2RkxFk4Jf3_cf}kcRS!-IXi==@3{Nw%n)l?+ z+-)XN&1d&sB9h6jZiz^y{@YuKWS;yckLcyfZET2MI-1ZBy*w2vT0{|+5yzd8undZw z=%5Ho@2|PYmbDSB4hWZ-tbU1b*$V|Tbh(du4%AXE_aN*bZC_A;{JSm$ z1xWFAOjt}t|5?r7@mc=WlKF%s$)<=%o{OGVpJ8c*U z_4Z>&D~5(3G+Zw}F^2)wQ~S194G+Nz7w2@BlwnxBGE856DIM+$vkc5Fpu@o94azG& zGa#x>bko#iKO|SS@69KX%kXQNdh`#Z3LYbuIBDJs?PagGsVI;~W{9p)Rmt zxLmM#JsOvCDcdRIk^oQD75OqBdLb?R;@Rz{eehE|??w3gKDhtN3=yohSB{8aO=edi zf~73CSTBX66Sgdqu55nL4noJYMb|iXL)O4fV3oPm3%lN6rq z8-|kmE4pvRFd%mAnO~e_FSqf`cic+Tv&(TS-6PXPZlxq>aKr?gS7~2oY+esu>ci&s zMX)(KDzy`==%{?r@IyyMUVuTIJg{RJY}#yB^pUF8>6Ny1wQV%WvQ*e@XEO+0-y=ti z)M=2v;YkszL_g@rrX5?hjVwssZ3zilM|#3X&Tq*$Nrx1h_WO%T4p2`p@;lMWfUjY@ zPZbxEVwBxFe7dT?*TtvnmEFtm>6*)8jmn6w6QPOHNKLjCN+W8oTG01%R3Ap)uOQ+I z`hH%sRmgG)s=q>(OKayNWVw2a^pP7rzu*9J!#sW2^W3oVR;1yLMmUj%H+S!dqiA?e z8DENe2aDvx2)*ASE?Xe=&LiH2Ux&ixpt_3(0OqZ#Qxw&FTcJ zm$DvP$j?i$eKI9J2#OIIiz@_az{?WiGZ{@T?(sW|W(-LU=*SPHmiQjf+qKd%9JkKZ408GD{Fl zmWY0hU~-m)fR(LY4=~MeyzM3#65GEw+r&2yg7c{#m?IFrri(d(H~XA1NAUK1@`8o- zq=Fjv$tiQ1y#HNRTRnNUco?3f&LwO;nPO{n#*eZfGIft&v{as(+ZfPu{;99?!l%b7BhIypxMy zd>oRk`|d60X41Q|B3%5_(`hh%JSZu5YZ|8LPj~o>&BB~U%m)T}dzZl;^SbycunDa3 zCGI7TgMiO_-{DaP1pB#03%?u%pTlyv175shfjgjg$$i`bXMI($$6M%YhCN>7xjoq9 zNgQ~II={-PRjBhXHC~50|E6!OCtln&Lai%A z2)>K`{ju!N*x&ap5W@bRzrq;3BKaFF=oNLl%zH)Y=Y5foDlN7^LMn7Ak%x#B*DSjr zg;4LSXCOlT>|hom)HTKRnEhzG+lSc?brxmJek?Tb#njBR*NZSU!=o&PshN=>F$|q# zB?e&VglEhWLnqU>U6C=b6#a#a`AVtTa*8o0h>aRZyH)?*g0$O>Y&)dgCbd%0h@_|X zq7m7_;etkF)6)VJmF6bTqNr4VJ_AK1j_DYzpmrR-ixt#T>1M2;Zu*$u6%EMUfmd|W zs}8Sds4V;AdxCTzItdGd?)BtW8Fo(T6xs9fd@q!`=u88>lYO6fUkt)=)2W?r!)d^6 zz2e-C_yLGIko|M%)_xG$wEynv;{#xrs$)z6OtCMwOj7`pG<|ax1u*5yKbg3lH3$!{ zmE2X~=!1dWw|@EQ1F-9%ls1K8D=ZSBP;3HzTS_2{y(VR`Zocp^WKQW?NRdN^w|{$d zsCPFVtg4@5+9iR@0n;wWy-P6dVjEK8OD*1C%aU>NS`9ek;%#{I3Ufj#N1`z&wD$bc zAu1;%IBJB|@ZvXSSPg&Wk|wKRvj3miB#FTT`ln6|9();-$Kb&Y`XUq{lj2-afRwix zH=qPa1(q|2SROyzkBH@ul|_hHo?148t~g(|7P{ix4sXyE&&X5~mRmXj(Q)~E3WJBi z{mLKq^FyOB*f+4?6tQ>`(pEl6>;E_c7q1`ByiZ0Z{ELj!*X50ZU?1(^TBjj!-POHD zjCTU$Yd+!5H@;e9Mv~Hg$GVY}F1}uy zM#XZh{zhRe=k&T%jO8r;QgDX~r+I1L!*H4d_wWQ2PFr@b83RY&iRqX(VpHWfwcMs=@>+?uAk#Z1j~7b6A>)CJLk~Vs#(AIFXd_lz3)O-OWM63 z6_edR%~3HiT+NG$iLLZ}!O{^7Sg_oA6M_W`N9kX@qN2oQ8N8(N+!yeY&Sd1fwUVSm z;zRjHnMxX*w0g95=P((es9CtX>^ONlrOI74*CENq@Dpxd_tHRBYa=&BK7Wqpy+o1E zU!0wmQRMRruWNh_<|KR`-1c>jod$d5bi9~DNzh@B#1ViSHedvtRkIp-z zqO2_9lr!37`Wv~w@|Io5{q<%naG|)r2KLOVNcVBF#LDSXPalTMqd!BQC=5f(l)|mz z&ExQyk9XD0l|zurRx%UsGz{WNg5-Wf>i_K38*#(gkn|8YoE(v2Y#1)q2x7ya{Ot=i z4BbsvP!V1Djt>>lE<1KqM0WUKYXwS6P7rOEm$*oohh~2NL>-d z>YaY4QLGNTVT$C#M8iTPAGDWRBl&Qd#+`Is$kZ@M*hQL=%~a_`WH9=3N#ISR#%VsFIlO;dO)`a=Oa=S;*-+WJ}>< z*>r>(7t8Y2Qd}%{!gd%qQfkJn}g+k56WitjmExKS)*;=!`|eI6f{)rMx*u&g#_xT8!bu>1JD zOt0nA4? z;7+H0=sE6mZ0|g=!KlCCh7HE!lx%D;mK&&i`j)w)@|h8T0!{0CY46dr z)(!AL)4I~@7b2O$nI{p+G%JlqB(rpnJf>`zUSA+nHbe(hmT$wDjejZ+W;zcYc)x?n zbk>F(!Az&v+>19<$B}sKf@j9E@G+g*YlDyJ10uKaG5xhK0N-PO4Ssx&Rr~buJ)W`bz%f~GT^YG0`G6dVjAeo3BKBY+^|k+G9Bq zw0jgGb^eYx5>jl}^1o1oRIvSgm8|N|oJCd1{EruyQ&sZcq5G(o=?`$DTBfykE2?E2 zK{WIg1AUFqSB$aRfxhBjf-!r5qSW^CvmvDxC(9;6QEGMHu7^_5jLau-7|qxd9*)tB zJ#Lned5%OT`E959!|k?(@JoIU+Y;S@5-rx%9Eq}J+-rkykKF-^b?y-Ive zpD#a;QHvUZ#~8KX+GLGUi-RG}*wyYB2*s{;;e{UTY8m~nF*Dja3z!*|2};Gx=-#7Z zsFEvuPDPd6Y%~#7@>46eV<4IBOdbZ3jWeh~GMPwbb}d3OAumA;$pmS|DvC@1>lJkv zy;`BSAEQ@}lGzx&3KdI7S~lz{iws4}^0yC!Q?#s+7aNvnCr350L`(N@!xByNuPO=} z$J{+p(3o8!fPw~VIGf)#g0x5!4;Q0FGF-2S7RfQw%Lt_?8`>k3Qq1p*P>Rld6(ptK zy*q@YbkebpNJ>lS_@MMMwkjW`mv0R>Ph4AF~`qtKXgp!T@4Oj0Oe}ZTmlB05Q5(6VvX)K1)AS zY4@WJMIWfN`&yN@Fe)?pQ|uUKMjPG?GN{a`aC0&yA%Z;CVG@E{MTd~3q7Vs2mI#-9 z5Gp{p?0y;ZD+(@)5}ZTl^5p&#=v+ogo1k-9wW%9*!;`TGQ8zR*K8U*E<6J3hwYVbY zTdj3pOR?3eO#OlZ#F-_r7(gsLxB~--;>!qkluA!MY(}Ycg`+e|rK)l!7|UP8eg|Xu zUh6wCmj6I17ro%+eZJ@go8O5*FZg@cH72MY_r#GKZkAhbyFJfhL-Vl6bpKD^f9ua%}%S=56EBmBQMV7$ zaYe$R|7s%=4#75FNH{Dg`G~mey9ogjmmLR>8*7l3b<63;jF`jw`Ha}+=f^N3cGV~Y zGvqHm7-EK8n@JEeS&bJl)80pl|YC{a) zu-LtP2|;rBPnB*5QW(DD#VaV4K7iLKmHy?sj8Z9G?iX0 z`x`kvOaBYV@ioaNVM^s{zBYzbgzd91q{3Ue9D~OeAJi~-+-M??!Q<=2k(ePT54OMz zdFhpFm?6JaC^kkVql(X2Vlv8`5r@erhs((X!lp_Iy$GA;1xz7q%6h2@NrpZ*b0isB zrWYW|z;nkR9gp|FZP4);`WTLm$LiYE7)Xu`v&2C1lidv%NS?i`h)72n|9V6^M69|I z>9{z_Fn)jKUMR-zpQgkk*Gqr01G(O+C{N^io401t)XVY56wK%$DLG7{z=wIfPNn z>z&(iRKuEIRye95%v2OdHFR@pVQkl8H!sF^OEz#DQ?Xql?zs>~Dt57yV5CB7TO>v* z)UFwjcpQ1=-rqo^<1&9AMmnyVaa!MuG_4c!a?rFE@3lbFTHok5Mlm-{C1Mm)uB`^6 zm}*CokrVvBdrp<&1Xo`PLr#$OTnm+iCSc#wOBjVdQ?>__(BV>-F}>1X8;0qXGuF|V zUYXsn8;J$$3!?}u$ZCH8%{nsf$l5V)QCt&w7kr1v$oG^lII$D;THe+kFk=^n1*LF;0KS z#T|2i#?57z1Du=C!yKT1i8Z3tYTUy}R=2jCM6&v?V+w*t-!EH|;L$iNF$oy5AV~1& zPSRJ5-&+UVnxf+OHTwrrsrbGB>@kGTpPi6K_*~3xJ;LY5SIJ|vGW>ibMk~Ku&&OzG zpW-Hj&aOP`Fr=WfG!a*X&LVEt3{k;@#ma|} z>ufrG2HDY~2u);1g(hzyJIbj?3!`GbC)&b^IW{uqo3+jN5tZ|0EoH264)xLW%%_bbHhiNu(c0~-c#hJaswA@ikb4<&9{qPvm za>VISjC4LrJ%y3ZUR@#(Go6}~k;wmx$g(5Df=t$Q%>Tu!@!iRXgD8W`c?&lkc77r&nmMz|g* zy2N9C?_<~{%!PEFi~y{c93k?eh77Ws{OzIEjuWOgil_2iKy12GNO0 zk%P_tHHa=Mhn;IXPx3!s!C}h28RyTjRLMlCa&RfLoVBFBRG5WTU7G8Erq8j2U5WF!ClUPyZW+ zHZ!y4$#F&5ZDd~lZ&T=I)VThcB1cZ4JHr#DdXAGVt#Bn5k0{Fr*?)epDON6)y!~n9 zH(oS9jgGa->i_sgypI3+MhA?!24z?uUHQ*XVX3;4Ch5g>{{JzK&YguE>9#$ZOIf5V zXZ{8L`wTi+mU|XaH>0`Sc=l$@Uyi|J>pG)iWZ9Rq@pv;crSZpb`7(=rpKf{mf1ez8 zf%+?CpKw{Qao-w$KR-3@Z0d!pI z#{c6DX#YI_6S3z~*J=4b)8hW;qvSjO&$KxCDEAj!+gaA+N2R~u`ez^@IWq2Ul+Q~p z5!OQ`Tv4&ztU_rWR%`>DQQyp17e|$tv2y+QKmWZ3|6K?Fy$$}m75sN6{O?Zq|JhE+ zlf%W&B9TW`1{(8~LDa#|T*gZG?@|Ax0}qob4#Z_R_ujx>t|fL<6hV}+qoQILY_Sjaic(ap zR7F%ouy?+_COgQzukZJNeV$*|UMG_&XPucjNw~SkPgOXupn`u@?SY7Z>aH3Kd5DXy zm{mV>Gxc9sz=A4L5uKSqOZfvL@NQTA)Fys>vIeC{@u0 zEeo{L4J`i7H*Kaz7xz{gGmV+0agAmUQh0cMO`wBhY1Jdb*w{O;wj|759BVr|2VOUK zs#Jm&)$roe>p0eRbg5OhcHMgQYK>}Gr+(dGL!F&nhnfYJwkWGDkQmt1!cp?gYi20d zQ!>{U3FDV_Wj(l3D`)+b%H$oeN~<=_U9FYFv!ycH3I2hRmUe-W6>Y6}*NWtoGtX%& zOPiXS8Jho+@@wS+TAgLRI(d0rEW^CLMogi4*og7tq280njPRN|Wa=1iFLep;$-_rX z4jf#ic0F@5JAO^$(#GR49n~=Qi|map?RdGV2= ze`%Ak-PFIgxG-(%L(b6!gwK;5r)j5XQ}PE!ttgvE4bW_2K=?Gd*>tU|wv=6aZ7qQhqmC*H@|@5K%ym?=O{*J#OZ) z4T!LmYIHD@3k{Hp8GFKE98ni7;;0N z2#J*EL!xDW$bESm)`fJyb^+sNI~@f;}`3afaG0Nv+sJtqFzN^OELcPpUV|Pv#Om zQGm1{0S~nyu2Fj-(!%VaMTlF}UX-*LduVauF16c|+Odb0AY!S#Bxx!3(9%SlPH$#b zh6;ObfR-hm& zdTyvi>PWn!C!9!~*+Xj+AE@1hv<`b{UE&M1(|KuDpFOkz@q^l3NgJ|CRzz#|&^83}ZA-Lc4{c8%-VQ`Z_RvnqANh7B zx)>W&b*CEnb|tzI@X+oA^7SBku!r^}kgq4vi#@bAfq456ec3~CJ3zes%_;u@+yEU& zAm2g6VD``<1o9n93}X)+P9WbA#7OqgQ3UcGO^jg=9ZMkIam09d+nfK_FWAnZzst@%}^1W)IcRp$FtUmzYPuL$PDXcRsOz zJ#-;~d>0Xm*+Z8Q$ag8>!ydYfK)lO|73`tD1jVa2TS>(#Zh-m`$agj2&mI~;Am24a zAbaRq0{N~Zg4jc4qCc^o*uWmTkwCngh|S0!4V#HA1oGWV1ha>3BarWQVh4L@2!VWe z61&(#cN54rl-R=_x|cw_`-uJQ_oJH+5Xko+afm(iFoAr-h;a7MBLwn2N+4VKW5jU+ z`JNyQ?4c(K#CwW3%^rG&&?Dco#5rz&o+ps6LPW5KULcU~MdA{B=w$-=ULmfshh8HP z?{(q^d+1F9@!mrIx48itNg&@l#9j8#C<6IL6EW|tHN8D!*eLx`JIN~9D=pzF0 zJ|>>1p7KZWlt8}Eht&KuZY*|p>L2s z@=Yh+qJcGo6#2d*kS%;BkwqZi_rwSG(2oT2{X~3b5B)+Q-mkpS zzTF8A_Rt;#^6g1@vWNB}5N~gy4|`}|zP`jt0v@`GK)!y&YW7fn0{I3IYuG~r3FNz$SjQe3L?B+7 zSkE51ff$JVH&U^Q8=#vBAhh8F(?`7f&d+1dH@m?dYvxnY5{>b+xam(1C z>TRl#ZzOStfQQ~CkZ%+b%^n&>CJ^rz;wyWo{u@0Y-|xf^0v`I4K)%0--|V5;1oAb@ zL-z^-9;zjfuSBY24>czcuLWry_E1ZL;-&jP71rDU%}XHPe5Co=Lkkedw;-tvduSm7 z`4%QE!X8?bctq{RNQ<+F+7gJ@j`^IEk%lK;Y$-`2;^%|N*^+*9_m0K z-*Tko*+VN3$hRVCCHBzD1mdkiT9tiOJr&gmImU z7Xta#A+5_ET8}`y^+_ABhq@Aa7A zwjvO3YoZN%Xj=mDw#!5Lx90|E2LkzaBs#H&b|#Q-7s8!Av@3ypyAj>lLp=!O+k@!I z9_mRT-d+Tv^+f)?spvx>-@ZgY_R#(W@*O}7WDgxgAm72n5cbfa1o9n53}+7=K_K3d z#3=UA(a0b9jv>aPfpr`y@*Pi1AmE{11oHJJCbEZ4B9QN7VhVfcR08o%Bc`*5&L9x4 zekMK4;s)qH1oE9t%wZ3mOCaBQ1a=x8I-fwk3y6j6p^FI0*K9GdggtaAfp~q0Wyl{5 zRG2L%knak@mpybPfqYjHe(a&E3FPZf1h9v$A&_q%foS2^66*-W8$`(LW#qq}4D#JT zY$V{Jn+W8)nb^V}x|Kk_!NfN9(Cq~B-9dz~hwda0?=E6DduS*z9$mPH*vk#jeFXB| zPaI$mJxCzmL&RbB&@ck|h7(8FLyr=O_ZV@UJ@f>DcnyU9BsV}$5ygIduVauiF*I1!j2oDB?#nOlC%_iXlVlZmLaug4=qa|UkB21?4jj}m(*T?v?6PR48CsJqj(Aos@bs??89$J?`y!A-y zv)7|&Kp}P0}13ih#1TsI)p&JLy2MRp~DHpJAxPqkNi=LB9QNBVhnre zSOWQuBgV6bP9Tu47vaqwI*~xWlZeUep;HLNJC&Hm9;%;C56E{0F_VCY&LWWSKg4YI z&^ZM1olDGP55<8X-}%G>_Rxg{;$1`_TKL7p5`yBT`#%*v+yGrhAm8Q03ieQ60{N~a zRlo!G%18bTo6oy0EoyU=aB3FI3}>|qbxOCaBU#D4bB0|fFtNE~7hJxn0q zFe02i^az1?j}ph&Lyr@BfJa0B!tfqYL9r`bc#5Xko|agIIoJb`=_0@=bx5Elr< zdy%-r9(tKTyjPI_Rc?S@BarWP;s$%@O#=DeB5t#XMiR*P4sn+~G>SmJ(L@Y;Xe@zv z?-BP^Px+&GKp@{Z;vswJBLew8CZ4c|J|&RvGa{ZnG=V_Ai9`~6XflC#Q;6s6p)Zg> z@=YaPqJcGy6#2d)UK8-pHw5xcC*HD$W)R5t9g)c%nnfVq_rwSG(2oS-)qkRg&)fk0 zLLlF-#5eZP?*#JwK_FZBpTsW$`Ti!d*+b2&)O^h}q*?+VDiMfRM`~_G`J;gfGYbOw z<{`CY549qYuQh32_RxF;^36|LfIYMzfqZR93$ce5CJ=8C(xU8(T2cPR$ROY1q_zY+ z)Q&*DB}hxMhn6CcZ)wsp?4kAq@-0j1z#dwTK)mHiE3k)FBwiu^N>o(l251!m`Bo*Z z#vWRoK)y9dYqE#dB9N~msS|ssGl6((le(~n)*%pYT~d8LZh+P&kZ%K0WDD;~G$fGk z5&GkSM(m-D3FLc%+MBS4HYJenX=-oA9x4dLd!E|ej9#x6&B-9&i}XYbQg~=f0{LF0 z_EzkntqJ6NliJ&`hqfhE_RxL=^8HNh{nXc)ra3Di>=lb=Q&pdGr5G5O`>0otLP8I#{a z9-tlSKnD%T>(vI+;44p!00RwLpMgh>0S4{Bz@x^%f(~ThQDZzoCo}MLkv7>j2Gxd1|BuW3G@L2j~e3x`kHx#{9!OKpuZV-)EE!Y!lVEm zig5s~%)p~Y{?JAYJZi)Y?ZUvL)+28eLm7C~$Qe4Ffk%yep(_}8)W{XOm4Qc%XrYJ` zRA3Mz)q1lAQ~)$Ukt4Jt1CJUxLI*MMsF5Rd3ImTCF+zP9c+`jyx=DrdM*|FUgdS$# zQ6op_B?cZfa)ib)@Td_Z^bG@#8Zkn%8Tf2HJs?Lk(%&Vkz@tWv&?*c(YUBuQ%)p~Y zj8Jz59yMZw4rAa^BSt-n84MawBS)w&1CJUxLW3E2)W{V|d8$+-1&9&q%D|&WjL=TV zpB~f(C~|}jX5djHN9a@r9yM}=E@R+PBSz?E1|BtHgoZJPasI;~N9bh+9yM}=K4jog zBS&aD1CJUpLd~c*06Y{iLW?r+sH@=qkD@ArTu~!OXcGn=HFAV@W#CaGM(A(`9yMZw z&Scif9yMZwUSZ&`Ab;eD#zzc1YUBug%fO>Xj!+FLfQKSR zXfXyJHDZKTW8hIE#%jpFDT4;o$PwC&fk%xTp(7Z0)W{J!i-AXt7@?~ec+`jyx}DJD z0S!>(3N>Y0nLI#_&_)bAYUBv*!oZ_OjL@MBJZi)Uoz9Tgs|GouaRmd98aYC@GVrL8 zBlHLZj~X#TuQKqc5hL_51CRPG@<)-uAXn5H>IJBl6u?7~BeXaJj~aPGt26MZ5hJu2 z1CJUpLc1gX?%V)Hj?j?|JZj_!{fB`^jU1tV3_NPY2;ITJqeiq)#0m6O=m8){Xk!K* zHFAWyGw`U9BXk%8j~X#TXE5-n5hK)>fu~xp=7`2%2Dzd}j?kkFJZj_!y~etmT%m{)RAmq&>Lv`l9tJr=yE5>okt1|C1CJUxLT57Y zs1YM{B?FHdF+#U7@Te(9y;>Y21CT2eIYO^9@Tid^^eF?68hJu98FHC}M<`Vc@COt2v^vHiKMIBS&a!1|BtXg!WQ23`+?9HGe!JZj_!{mj6lMvhQxQUDJ{ zj8J<99yMZwx-jslDMr0ov>^kKD-=0G`!evTkt5WLfk%xzp$i#!)QAxp#K5CQjL?0^ ze;+qMkt-B=0w)GJqHf8+qehOGXe$OD zHFAXZX5djHN9cG49yMZw&S&6JBSz?227WD`M2=|O!@#3Pj?l9VJZj_!jbh+YBSz?R z1|BtHgnnhxqJZi)U-OIqEK8yTOoMVtHYUBuwX5djHN9YR%9yMZweq-QKBSvU`QUDJ{ zj8J_!dH`sEB1dRF1|BtXgtlkkQ6op_00tg4VuVg&;87z+=n^A(wE+e>LN_q*sF5S| zAOnvYxk8aAaA6Q5>NX5KYQzZb%fRTxHUJN{H#jLHDZMBW8hIE zM(BA49yMY-kNjg8G@wR~&{PH;u9MO1)fk%y8p~w@|VUQ#0whTOK z#0c%jz@tWtP;Ukv^+M#2ViAK}Q6opF%)p~Yj?n!KJZj_#RTy~Gh!Gmgz@tWt(3i;n zB{x8kBlHIYj~Y2b3z7nOC~|~WVBk?BMrZ>D9yMZwc4YKufI*JXK@2=<!HDZKrV&G9DM(AM%o@$!^(NUohc>)IpIo2giiu~FUCPi%h36mnOiG)cJ(_+G; zh-W=vQp9oq`5Oyk9ud@tT%d^IUM@xVrx7MaxBnz>QgpWsVN!H+MZ%=$U01@S=+#c0 z=|L?>(Vc^HDY|hgVN!J8GQyqvpoRm=aczf;vXh`y_&f5zXb%Kd9u|N4ch zDck=1LRGp|K!lCKsfgx)My^prV`q3$RI{f-z&1-+6wz23+#YFD?eMrFpYeyd7N!67 zhq%ssR)2`gZ@M^Ku?LNvfcc119QfNg?$$@`7xsUU$Cm6LZh)p(Wq!M{yr`u(m7WsOzKa+>h)G8*~3q_fPMt5J@Ybj9Q;LK9*= zuA*^KjTwI8D|gq?4~^N$x7;)iQi!9*km08JWu+Z#Ww@|b)55|~>ZazbMt1BgS(~V~YBcxlU?6xqgw&n9q-`n&TQ>eyz1> z_fj^=mBKXB^A6i=+`&a#HO3{S5Dk8Eu9#K|*{TWjs#r#rj%vD&Ys|};4ky>^)B`s4 z!kG+He;fNDg}szzY_H1QQ>4_tyQeYxer2e8BIT%vJa&1 zyodFf2gZ##NPpQ_iNJhHS?z~JL(>l$zv9yV`?)rjD{14#8h_-CsQwtAy5g{m5B1g`S$%L_49>Y+ly5TlA6VpVz7hr(AoTYup+4I?#dlBxj7xE+e07sGXkoM;YUgE#*Kz zD?Q^b|n)Z2$^rpP_uV;UST3Vd_bDiWjZ#6d3lXBWW z!zb5O#JCSzd+|QV4lT_swH}`Gy2;w%fB#iX*=35hv^KT3+;xg}jjg=qmf8RgH;p+ZrtnnL&`3y_vYo}NGUp828x3;YH_7%o{Gg}l8?o%>l%C2qh z?OJu$R?PjYov7vP-C8GYpGvZGsCKmWP=LHKRNJG@p#bBnc^zivW*77Q{q+xR)Nyvh zS}wOo+fg$`p0Gz-QF|y+4&0-yUE6*+-EZliaiPST^eZ~_ud(__5A?Uxx&95UpZ|pX zX^%Eax)Y)`UJkWeR?7+dwbP|y{#x0&qU8YNUFXntDqd{FCPBgNlWSNzL(myt_HXsxA;JQCeP)Zeyjxry&O zrXiGTL+ZLsSE|n3Kc?k!Z5vlAHOb1m|DNQ)Fg3|vR)*cTwf>c);=9z}{4}k)avY6@ z{Ir7@4fP-XeH~uOJQ@tAe{0`Wl73s#T;|{RwBG~rG}II^%&#PEb<(CbH8}K?ifgr> z8_7|9q=5m;9JE*Cxy`RB+Pq3l`_DmjX>9C2ucngm{5hbrvi{R12Tav9l@C6(w3P4k zlg4R-+~mgnrLufqof!~57eD3cti9UKU^774ZCxnxHvXlHjT`W9uZT2U8zs4GH0$NV z<0M?y94D>DD+^AOYzVv#-jc0G{ykQ*kb6v&`fBRPhbKyHY1`T3>4`dW!AVjT zE7RKR$YPSzK)y3cDlV^_Bw1_f%JL*>kj6#+VifnKQZae_WT}J3SvE|TdgZ**MYf%y zE;s|?Y>1me2m1fm>r4?T-}Rx+nKw-uO}+ARn&d-6V+QT&)EQEF%|=XUn zl8bz2v}92vU6@ulQvH`9oaIsfgY=^AGnAfC=$E>pETZKX+JD&O2dJJkUbYjcJhP8k_|QJ7a+8i zw3uHYwzmvxrsQ&B__J3|7!nx^B-fnh{zTMZA@z|d&_5B$Xg2k~>AZ1~^%H4G=UXH- z%6ZPzNzO9ux2^28NOCP_O2X8UruCRkw2kt$MRexaZ}?Zre{yqQKx=YboNIq`!joq# z&K0$B$I>Y3!+1$X?w1&MEEVHwB|X<=iR6YTmPk(WiLKOOca}&Faw@9dsEP+u<+zk8 z#{lZdF2VFLaH&*P9;l~2-@25Z-Hk=gW09bBRAo>btucEk1sV^RCzk}!7GbJ~ByGRaBfD!*K&UNg!rrB@DIPG_fj#@Nb!#yWI4 zEkzxHr!&yxEGMA0U!gAQy@Kj4##$W@^HxYzb5b-0e*+uC2v!I0a9JuWXRM%OnulwM zg|E~#=L%Lw9^@;v%^Ad|mmBq!s3R-Bv^RC+cO=F^s*X>`mDG9m3u$eRThYHO)m@_@ zSVlg(Lb8^(u9RHqG8(s1JyLbBby`K&PZ#6hI_tHdDc z51wbwej?YqeGIw|-+GlUy-+iV*|dmbtK1WDJ_v1ss=C4Y@^Y%C3;zD=DX6_E37 zkgChSAJSF(p08Bc&}D-(Un4IKM6Yk8wbRXilT?;O_|uqavq`f1%fRraMbv>o%LSv( z*hL|DA$N6X_#L6uVN}rb*_)&)>X4w?X|uZOK>@Vtk(+6mc{1%sO z(M!FyNGEeTG$(EO@J4#B3ysP`9BHm=%c!mDwME`aJ3yxhxttXkyFL+pyopv&fUbx+ z&;1FW4%1d{mixIs>GulGm1Iu(IC5KgYwnl)37>j`ra0?zZR*bmco|GLUCMD=u6dy8 z*+@g&HYr;p`vp;7@7_iom$5@~l@A3`XEqDLnXru>5VWCxh_pk40Cv)~>GwuzuD?qS zc+f8Onzh;}$}YM^Iqs#`7uc-^R(3ZnZ@-zA_uMTd=46s{+NnjlFdoYr8h4Zl-N@I_ z3+9Dl)?f^x*&e#+xyrrvsMk1wzPN)X?&jadTJ2VBo!Z;(rTua?cAKBEDRghHk)G2B za<6?D=>~Zpxg#BMpXA(jaZl8Je>ydC+kTqO+7jq+!%qX|;LI z-u+FpW~9 z_0nYRlw@i6eS=c_pDUCPUqNQK)V=+3i{2m5UW-Vntb`q*SCi>uIy9hfk6-N!TFEPo-Y!tL2YRrAAon+5c(xXl*o!c}BaZu9Eg9UYbeuJG38)SKHOS zjE~RNU8ajtq->i&m-_hU)LqXLq>|KK*$H&%pcMos(FM*lM2urAg^nj7i7r&8X48E> zQZAS*IpuVwsrlcx-KQ8a$x>X-VmW=xeXh#w=jgPj>d?5GM91KlBK4--QK%_ryMK0H zJxs^vY5={S^Pw0jzL5S=k0Dj<>bt2@+1jRk&e;lY)wHvw)925jmVKG42TY66F}#%G za`q(W806qo+S!3=YKPBAqqpevt~@Q*3XCHxk=};qc_rE6Vo~;ux_9m*e+GiV=Z#d5 z_niVvN0^2xZ>2B!rKN&CQlL*~@g0Iy9r{T{{PU1U{gguUK1eiv?C?2RGJe|Tm@4sy zC+dlZpII+a+YI%!h_aDAMCi&M<9P*YM`$?+%(e2SJK}(QqbC0MUVcrQqz-nchF7I zdO7`l(cKNPXt<5`v)kVnaO`MYm*L4JjhlshVuH>tZ_uB?rH*uMhUW<{H7pO=ZlZ3w zwtjEfeAQFHhLgWA)R;+%MtBHjFj>PlD(_h~eb~zs}vbya@JgvpjX|YmxS~ z*@fzk@5Mml-irDIR+nk*nI=9iuIs-=$rPa<Eq<(Rve;u>p=i3IUH18r z55$j*VPoptr}gj-6pQFS;m+w;v8|BD{s&c_h;F>YrssKwM4tXd>r~zy(8RcWjXy8w z%suko4GiEsY~*)~b+-Ed?k^vYV(f@yv0L5GR~%cS^55g<<5TK!hkf7<+iN^N<-Z+l z3XlV_7N$fz=<^jd=yywUj|V-E1Jk(hkiC7>#qRp#UhIqRUFAL28=II$nTPDYTwUz& z^4yEfF|JMdvovJ!*@ZbHWc*ss0)x}nC%)QvDmUYVf_BndibSTts zo7KgD=~{6M}WNB}|84I;Bmg zajc>wui=wbF`cBQle4KDjHmhBR7~fj zY1M<|ySbk>oq>N|osFk$j8_8F4za!v6J)3D^faG%it##`$WLQ`2hr2*`8-oh=U2|r z8ZTsusq;*ym8o}3ou`N?ejQ>zi=VQBtu5wYO`SJx@P?Y52YnJf2&s=n$Oxu9&a z2$A)8y4B3rW6qqvoYQ%x3n&fu+)tZ2@6W3f@U(_k&Rwqj*>=GYdb+!M(TmOLyg&OZ z=;?+#`EX2~mvgl0%bw(Pp6T*n>K!`A=;e}MZ@Mwkd^3KhZd=|q{H5ebU!C!`yVXy< zy-G`|fu@&b`O{`_JD_W>ksbu-{`;LB{x^J5fz3L66a5R*j`{fv4=(F$N=R4z zv9+8@>+%(19S6NbN&DP`h4O! z%U1H;*5=&`nD#^6u1i38ecemj0o9FvP13Hqai>PtG&jCMdE#WwZ%~#|lllgwYI}31 zf)2c(`H>Q1G-l@N|5{V3P}|)2uI1KHbHk)Pc-ykb$s7Y~g7jpqIi+m;D!NoH<5o;Z z)sp=%Cu6&5(mmwnzn5Fie(!BsSEM@j^5%N2fxa)Nw=6~KnE!R~b!=*G9DIhrKIZi0 zwB&+4${BY>hMWHxcNIqbeI5P0)pgLg3-vXparb83-U#^i*qw@(D1l(VmcTLTA`Tz2NC#75$SfC?mZBH-c>0V-2XnWHe9d)JZ zLy-&PKVo$qT~nDSL4MYabfr3_sL>LY5UIa zUqy+>J=Q%5aZMJ>hkag~6cemhY*rdNXRUfCMlK)Wxp?#y5x9Qe%*)GP3-4o*-+yfj z65ke|_%QnOE3xagc10tXJK~K`QsyPAR5347NB?`kbK+pSmHUv_FGWf7D%U!!PZXuy z5+<34eis+{0H*iL+oyA(ccL*LVD(k4>wEA1BD~cd)W?gNJ37t3eEyASv0&ZdxPI{> zx@MnN85=)~@}&a}ndKA3lJk`o>&|9~f{lFhtyuU-xYl~spo!+2@Ud@y%D&BG5wqjK z(4n?jVn>0zdtE=C69a#JTAh0Mi@0#`^zR#v(W1U(V$GP@nIbMvc=+5}(V~3JQui$F zSJC6ANDkI7c_<$K{8&7s_dC(jrc>X0O|OcE(~1o&d@@DME}wQ~;+`lau|iyn@Nw_O z*M#tY9L#r$PD9eC-?^9}o{!)CxXrjr%9}3r3V)fCCDs>CygVr1Iq_z_yvE7gDB1s(OjH?vO(=j^wk+}Kf#)k`q zK8fvLzASj@n3$9KS^fP7{WG44z6X5HR^RtoOr8Gt`V3v7h*@~Pb!dl2;@i~nU2g^? z2=QZV@t*hJiIu|^eCuOzS?q7!Yq7P*3$d)d$D4X)i9#IFTp!xwtr$45t^RP)N)N=U zhWoFlY)ukF+fCSZHaJlPrd0Xn_x+_Pz3ue=$f1wK;;k9BUucYs;xQ6FY~bK~F3Dnr zaS$oq_&^}WwMoE4+iJ@}3;taP!> zEt~~iPCkBw9_jsMjM^hLd?sB(k9ZC0^$|Vz>O;PT=)su*))O%(i?){3v+C#UCF-E; zudlq>2d75o-SF)$9U~M5gx0%=@jA#E{9|N~~V~NCbSZ-PH0;vbemviNl+( zQDVZdh7a54dnjC+-O)?tSH%9MwP#mrlPu15X?FSMg>A~xmLkvgHVLAIi(8FLp~r<> zvxKjqaH<%wr|tUWb7#eXSwY_`w@=iIh6VP2J39D^Nc1gZH{sQNOPaEYC8y0Uk`*ob{uu9edtb6L&|<}eK96F>Y4y6He<{L6P2UbD62(%!P&ii2PI}k# zu~^90h32c$X9tyw7jr`k`n1(w7ayXg^e+4)PNX;T@4Y+Xi15gJ=FHF%&xFp?V^Nw& zRi1b)T=#hUV{t>jdGo7~1YxH9)AZ7l1aa2ULYXu;P0TH1A^)hBB!=C8yRVkM_;c}E z`z`h8vP7}dP%U4i=QgE%&0BA7HcAl3t1O@KVC^}@bH@5gp;HsZ>`JY)4bC4?Uc5P& zA6_O7ozRsZyhtH&v+U;W>_9+)ALUR26A zyGEqaH?8QHy`dRGnqa(8^@=#Kai>j|ILKGZpIvL|J1@2SCN}V;bk~^n|2(?*QB3+$ z_*YA-OJcG1pYmg>e-Y(x9%$UpJVw;qbg@Ryb)QB0!ss9U@*BjRdsl{K9r_}!@uhY{ zt!+~_zI`qh@P#IsudEyRLX^r^(&t?r*Sc>B7MWf2#!KhAVvWXCUvox82cDT9Hz8BB ze7v_(k9qe*iAkR==?oIRCbm^CREF>E{kHY_COYwzvfb0XYNnIvQi|)wysx){k$Lt` z?+Ixu6U5?i11CGf~VK+q7gw?FG?kv@S@v#*4ih$J{J`FHMxY@2?Jeec#a43Hy_i zl$SpF)zjj@*P5yJ7hWiH3Rw+pyYrFw95#Ap-oek6v|IZ$@eSfc>>X=uR;L|`Z^+#7 zt7jz$tCLIg>3NQb0bO>N9#r=~1Wg;!D^A&7>UiDkrqQCy#X=A7=6S4` zuKjD5-)Pq_F;LmH=f=cJCw7R`K2a}2FWpdP@V#LDqgS&!u8vadrdBoc-4`R$%M3U& zEq}D~f$t4_syyzNl%c<OP`-bPsYyVMR=YT8?5)g2Kd!Y0M$Tf26h@aHSvw)HOir+qsnhHdJ< z@3eoE=v{Sc(8WEM#96*BB=`6>Hcd~1WNkhVbx>8{o6R}CP(<*}$oW@Zb&NdVn@N9M zIA~DKT$iB^Dv#gA6ELWfH%w9oRTaK@b(yD?F=Z=~5_lO?yncMIiQ}7@1Ko`DnBygE z9iYzH#_`Rh7T?_p*6rU3bG)jWV0DgHpm23{j`xM{UZtmvOvaTm7kv?`q^F2|*HOp8A1rL}l?*o0R>_I*s1j zH&Wc~bL4KDFOS42J`2ulT0Q-y<3+`d&yutb-d8G)rfWWr^6b7bCwqK8rg-mMcDVAD zC^4wW#n9L>VWR(wxFnCO&%`+2XD!oC1_|%O`t5CfUc`$pKfAxXD5nU2zFpqj8o#0E z#z$fd-%fAGH2c}bBS}o>yWu;t`~NuD+!k5(=F{_xN)&aMwEwu{dW@KLvDdfA*-u2( zp7V=V{~0Daj!mm`{dA0oyB6pgb>*RO?IC;l?+c zq0Q&7y5o5;=VoL-Zf-zEg+wus?=B9P`eDdO&nCcue@p_eqBK&5rm$;DC>FqlJ7qY2l ziRzUvf^YZQI|~x`C+m}x-aH{l-5E0nSF(P5KVN>xQ@!$yyINa|D_>-#S_N?BE4VSQ zdgW`_cFMYT15=b!7M(ucto%&e>~*z%^2tO|l<)V^M?5`>E`6cI@_eB1jytiNcAZ!H z@a^2(%;oECeT5fF{)qN1R{VM*Vgu$lN4Z>8I$Om*J=~0D!8L9ydh;nviG5LZ@Z!m* zlzi4tg3ZfhimC-?55D&>TG4+BvS@idLVQ}@wnD+750p|fx_GYCCyO~PC)b7n9FFLtk4zx^?#P=Qg;C(572 z$Q~;uZF_SqFCsm=~rs+6N#zOaY@CKMCJS5t#uuqD9?%vEF0WAOSC)^ zG^|nEV@li7hGE68rwePb!LxC-7{%hg{^*`1jo*vr_hx*UbRtMOKl$*k;j=SD@yOth zL$*XJ*@w+ zS7nNo1CH&FSR5yOc>*(Nt|s>9vuIJ#Y#AGs$yc!Hu2%m~7yv$4CoI!RrB zzVl{GQuRSRV|lyaUL(v>U0xKNg-K`QTi4V{rytK?9uyez5|h;7&8L33O_S7T(bd&S z>dAzoDSq8AC{+?ITQ({XBLX_VFP*XFg*e{f*_yh;62z(n7gnCpMJqeo&e^bW=VP&| z&X!^&rrZ)S+plWvqiO!XFUxFXpEK8$i%yoW&UK9w!wc`PQ|Zn%(f_wqHQA3YC_F*w zFfB8oaD1%rb#U<;5uBw=c$xP2#*lR)j3+E@4~*MvIR01!`Uhu!49rw|H#@F5|K+y$ z$`cspi`Qh#9exXpqXEno=C7-U(b$gPibd5b#A=dJe z(pvam_#F}}e%v2sd+Sz`l4(0FD{IperPGypG2<*BDZv@bJ?z4Rm1*I}_IuX4r))CI zyUI2#L21!wV5hcwZ!065my{l|@`Q45m+zy9k4a*jamJ(U>6g~K@e5ge$Bal%Q=+CN zHl-bZwNi9ykyg(tAX#ZUzkZYK-)qIVjnf((FPE%5Yx2bZe!?~36Tdj&?)fAoBmS)R z)3!)an`ca!&8m%XAM;%J^Nc6-=lqBE>7gQtXGA{hce;$hp`wKUNpt$3?fzivKK2L7^6?-ERtPo6SO8?nA*zg5Yi-38-} zr_0?V=X>=$k~1S}>b=ml;GI1p(D!_w9qn%^|Ae}Qy>Sl{Rew~-d#Ze>Ql-h!Nm-dO zqQJMuwIh8a6sLkwR_iCJGbX({Wr~V5B-NeyP+0Tb>Q%Y)<}Df=7iaixHnV9)Rw;+0 z!fis(+VzJO5z%My$UyhwqC}}yfkF9WM5PhKMvfaCC1Tf(v>#%1S?st}`$x~}x5N&< z+bz`fiS@piqO@6HJF&#OXJX0L%hfHOM2Q0f^jACfx%^N#UHbAVr4RiXNKlz_Q;2#eY4k%Uv+pY4rLt+c=-H^(xzO^Pqk|#ip2+?&Nx7Wc7d(m=cCsWM8{++ z^}68r?MiuE7X}n+tG^FQrqjBOiVCsc&pL={L$TxG;R{W~W{l zMs*xN0vCqUHh0tuiM?An^+IyD@+nt*=ybNh@e}yaY2CGxL-3)~$0C;7@u5@Ki^KK! zkScxD_d@uPs^FSmweX=+>tz99_|R!qlO|p8q0^O$$6o}~CH+Q+x&HW&N`EtZRW?4Q zn(ebK0=M641D%WF?RMR|o15bH`~AtxnRq+CZst1Nua%px&ZgjYy}`?>H*UXfU$S4| z_8ZdldvDx+XLneu-hP|kGdSaR{h-X!S$O+>b7kHgh0>E0d$+XV@tLvWR^6*@OHR$w zE4PQFy|k@$N_pT`uhFVqSxUs$uqVgf1c?WY7a6v{&r-Z6JKGh!xm_u+=WLAG;!NdD z#M$y)u3iy$KOa5#t=T)p=bOIs_YlY6A{c4_0;=Hj>#(&Xr%lf4fr zqf4|cT&KrbrC?z1I!;%Dm5)1%^v!%9DZcR}^4RxPD^iaph!~zT4!=9D!wq1cl@qN^a_Fq4Q2@y5o)Xk7*V*II3RbBNFtCbJh=7nqZekv*_IQ8mizeqH+ zsywN}$%LFqxINIPns4h^bq=Y=By#eaogaSAy(`Y~ zq;X=h!K2W*D>;+IViElvJ8AC-j{^f7(W&iiaZj{PikuQIYPSv^dHC^d zF)X3g>O(=6JTojy;+OK{@qPVhrS?4|DPKdMFVf|85ABj5E zBz;%QO2=&$RPv^7Xzjk1mvKzwxM;Uu9v;@Nsb>Z2gb# zA;nXat|4icZeG5sEa4ea{w*$}x8*sdtl}BbmkN)!9X|3vDZSY^12S_@D;vHjQVDqT zanAL6u}W3%sjd3VzNs|IuRlL5ZTVA0XyWG_NWG}s?^O8nh(+m&>**5Dx?Z@VEa6$O zU)?F~zOT9sD{IPn8tqQNFhYf9*f6d>`QZ zakC~(x$>;LT}Ib+io-MCQijP{O4Sn$k}>Fi>5bE1C2;YeVK^=JtYa;(Y-W_|EOHt~SUzmSphpWQVbJnOE z)e@99zs}E(3_GU8FO6H}_9Rwm-~Mym)f=xWuG2q!I}sA4$TJr@*K0$6LCdq|?^Czb zJVCejWS&*mJ3HhM?*F|H{WgE7??U(Eq+5+}zjvx=yS`|xWTlaQR6D1a29fIQvTNL` zMCHhioXoR^58NRyn1KS&r77dtQ8@ zY7#%BEJ>-H-fHyV1<7I&zj4@AdS=*_ z)mdTC_c9hMdw;ke@b^6;c?5@8OE<;vL zI~jgYv7hf$>*~&QQG}=64OTsO^>E1)ZDy?2tMh)xfV&~5K7SOG`Hewr+L>bcqteCd zjvGeCl)S2ZUNgGsjfYv{TKxsr?$&!ZxixiB&xBZ@F@*I)Cuz_XS&Pn%Bexp2+VL&hxZ-F266xu2Xc5b@OK;fZqqajPLO6)P@8R{`U3I--T|8ll#lO zY-pb>4i{ax<8I%aX*YeP)1m?1tm=RBynkv9>mGQw8o+N1&e}9n-`zTEA7|p-ZN%RB z>UlhEt*yYD)pNQ9FhjEb+Wh7ME14TuVkvdtWWLH+S2&GbJ>lSGjg-R(zh?J3)ILIb@ zkL;DKN+|TZKeyxg{Q=MGd0w9Hea`24f3E8~tJKUAJFo_s`z`Rl7xkZpFk9!8E?D(% z>DvO6@A#s}eYWbInMh0`GvCc}5FfUz7--*?h!+0-yC+758o;aG7fCoZm!hjn7ews2 zIfx~QaOe_xvi?>~6h0@FlK$rM5MG!ur4ufn-ipSyG-HmcDP8a)YR@W3w>s=bbb#aK z8@DD7H(_(T7pYs5E3rU`mxZfLFYeOchhrYZ;ul%jGQ9J@IBn7dOrEL9tq%yEbXJ;@P&F_6gjK#~igm zr8l^~cZw5LvEo(g;FChA0^` z?{fAU`XFQ3nIQ!yU-OGAV!#WYiA)I_LG}+Bz!O%zk8Xv7%sXKv!w*hSn<+-=ypFJ8 zPCoPa@ZE6oop}E{7*4)iqGTF{HO`;S$-^@$o@~OW^3i_2vS+m?D)4OIDP?2k_9QcQ zq7f^IXq-R#Ivn|A|J|t2TZ@y3(s49>fBSY(HBLW!dsx)o2lW+YtC-i6;T^@)l#&@0 zsVv{TDIEpNG_5|Z@(vlU!N)Csyv4_fvQc$ERxKHyhX?-F?OV=fhLgw3sK)Cl_$pC4 zKmM%Oy&DvcwM8!Iue$Aqk@gwU{2#$6p9q=VGQ;V~YwM7BudY(G|4l6W#o{u1c`>d3 ziL&u}`?avYH4W+1=JFN{UdMZCO*0hrzMwFobj~Zs1Z6svq9YOoA9uYwf|MPtl~o(7 zkro-T5)QqJu!?F%+}r~OnX%V!WPI86n>1UlBqLY$rN=G&ziZK{u^p#Ij(Q@8v8|Vk zw$k6f(i%2q1j~GR_l;!lSLF4CH=(HV6%y;w6lG6oMK$F5-S~rZOK#RzbfQ1-`byI> z^u~NjIi->7ct4R=8S~CA>9NYgH;FRRBiD0y373VI#8)JB+u~hFP7b#B7j5)9@s&b% zi59zIV|Evg%C}qmnWOa_kxueNnn@68m1nHRoRni|N*NUpsg|vz#2l?f$8Io3>$1S% zVK`YWi4Ss9U5FZI)zrP8!A`b)IhozI%g66#?3 z7iI~yjNSGRETO!K6l=K<`T!P_NpCfmz#?;6Iq48-wvH$(M=>dTSWH%eGNNc{#7(rC zs2BEyP3Gc6+zXq`RVvmsu*sAsQtV1wQ3A|_u@)N_!4O$Ml+zO;&E^tmrQkP*nF((Y zWi&0-x_3PN0W)N$FH(jX(&ykUISPiz!YM6=uexaMj}l1232!Xl>6*3S#pEEWCemy! zIjA_6xsI@T_v4l1peZL(&89RaxL70*e~w+y!n1q;U)hl0^#ecG<9EGy{!#rszUvy0J@39`{q^qPq*7NvKn z)_=fC-@P?bGNDGgqI?o7SVI;bx%jE5&0T>W6TLUvUHKr_yHBOI6{z?!v`e_k2ak35(6=-HJ!i zMEga~c6#%;<|&dV+Aybg7T?~hfykU_yO|?}#hTVxICj4KkEb0?$Sqq(@z}>N_)OiG z^j+0;C@kC{c$NHDeA#8Ug?M!_vS3ZOa8pi3ls(aY@A}KbsvKhf6NfS^4t-*5(P%Bv zcFqr@7Qa%+$H%e~cK7L(qbBkB7J_fH@XptoQ3HF|e^!HK%5stUQ;s4nP; z#>Dy$h?i);+T3cV5rst5Pqd-glN^~s{}>8gBHB*l;x1i(N@GoeT!w)jYWqA zC;PSrG^07?&*YR!-XM-Iza?A0x1pmE9zyTxB2aQpzOy*(ps){Ac&V|4ibCS6dQ(fS z+feBqhmx3nntGSbI^|)RgEvI3*=7Hn9=nMy$lPUEw8N^L2!}NMZr!tfWb*&Hzm#w3 zMfcWvTytlm^!}jFIAzmUpQA_0apl**xhq%X<50chx3et1X><8aW?igJ#ivft_{D* zQeH2-B@R2ZE%QBUJB0t}b#dusB;jAn?O7A`XfR2{>uAfKvn}*Qw4I3Ci#OM9q)s<< z;R2#guU~oX!_PU0AK}?e>ju)W!@7P^p0(d`)k1rvQu9!>SeA1OAe!a7i#-9+c&Nk% z0@Ha&#H+LRC@>xDP1LPbLFrpSG{31SahlsK!5|%UOhf{Z&h@m7R5>u6xs4B31Jjvt zMmiE^HaXLMvtVX(|1PKwGv4I;DfOxm;rS9~yszR@>Q=6GX*i5*&^ey&bX1rSp zh5um2>$!YhJ4R*zTl39b1tXqA&DWVm&uPJO_Ey*3ZzU*5n|d}`<+llg}Cd$9=-r^`Ay%(cur@n8M0Q7WSVIkZ3e7BRCEzup^&j^6l+5mBe&t>>7}Y}9?e zs(WG?I$i2v`=+B18xHR_>9wmvXFG*D+%w8?WaXwB+jZ&K;mX$6OT}`LRAT(wFx@t6 z{&>N)f!Ji6X1x0F6@|C>==Jc9OYD#Ef`HNWhh(bolU9{7bDLZopv>(Y!jg!o4?Eq{ z(p}^5>0cerpF_&A;h^3!fW_;t^}PpJ%<6i@65w)WYbLW!$NXh|0GB^Be(r#E``wMb z-N1_RrOV8EeajlCpayLAtXyxT>=9BZT%86p;^{5!C+)2Bkn1I* z=d~xO3M|_^kbSb{Evghe%M6WD@`v;zaT#JC^>`(qR*#o@)_uC`lZa+`rEQWAYQ~O4 zZfiHZ7LgGu#cC1{B-bAQj`r?qP1)g=iOY%n4pNZXZBf;Mb#BNDRVno&@8~g^72;p< zDPsIyIY-e~^LB$Xd%&fm}R3&zW^c^d1fEW;tRynnm$=9^J?4f|3vzDL!l z;OE60g^30%pU{*N+5HXm#)V#|qCVrFo6G%lw|1hvu`jZ3?XSQO)C>A-HVmL(w+j-B zLulMC;QW~DM-Mu2y}#-SdnJ}9YJ0}ob2Zd2k4$t_@<_<{ImIZ{@7SS=p<2{PK% zw`xQe;?>rt>r0k@iS_M7O}YCGR{7SWVqNKGW%eO7w_yC&oa6$eN91lwp<>+cacek_EFQUbo;`t#WX;QN^k8%Kce z-~1+YCbX>tUnq!;c+XLXPUKX7zq+*>$&DJnKrb7RS9SN_)4BDSSBqERA!&B$>1ibC_)d{p_hcy_BC?S>_4dw?2}XE2$? z`SCEoYPRk(>iq?b5cjl0eCN1?q)>l4TG%?r_)X9$>Ya%S*KYtG)av zw3^0hsD0k9Yn(e=;(@=OQ#b0F=f2}S8=Cs z7S5&q{#)zPiLX|dDI&kum#T?=?#e{t*Vgp~9qq;=b%z$;XAeSa zr{p+=Z%KIdciSeR2d^45OE-SUcRuxBK)(Xo*^jDXneA z@3aRZwX$pRI%i)l&$MQ&c9*B)m;3i^+P4a#v|(eGwSug-%Xpql9W>fRY&feXI5*3k|Af!@_}^56&9c=1mO z_yHaW#LoghfY=$QWbgwV7ENRP1CNOQAKfiZX~5Y$t>3?uC7gz{d0O9>JmZ=NXL;;U z#vC}y>#x`A!O=WW;~fM?v$E!%6gZmSw(Mq(=7bLE&v2Aa>wlGa4wIRop-7F$_eSA} zR-4Ze|H*ruZ501IIpuuhrp{aAZBT)wA~asx+G2D+Y1iXa_Cn0P$CEeu-(z%L*~n}E zP#G?HWX69!<|LZd|La2h3%iD6&|MM8tj8bxr|*60oJAj;evfp@%n!LOdxuM` z`=V1e-ba@|Nj=`x{{hKUyg!znU*U#X3iyhb)CQx5qSy;^|IT0?qW|ZPAN$-li+1x8 z{r*#xi#4d@4t_`U{f{=WhjsHQ9L}z$T&iD;L}mFZmt}h5yFIZt)Oc%<`sKQP#TyNg zi1bYsyMb!-_Dt*C=A)0%*=hZ6U?^*9rbN45$k^N{C*GU!;0a12WA*j%c*`><&tap5 zMdJR(P3Yl!*W8SKu4t5+YhlaLigwTaxb1FyAaao@G8ijrMRPJvcKx{g7O{}A`>ywC zBM>q?$8?z`)^6X85g=qVjnrYuMIkvlHVn!~=*9RFP(Im8C(A&|JllSXQ8IHXWE4Qj z1pj`X21>?2r+`sDs>v^yt=6n%y;>d-RoGlMhwZ0y6H-hqO_kP*M;F`q?!~_-MMqBFo3yXVv*4%YeV`#A8>SH#a9tajfo z1FzG`%Zfscv`>FL9B#+nTY=2&xOnoNFu>{DL-sCkJD&2-WZ?AyF~?fCO||NGFtB>W zGygB#j-2&!%kZv|sBJMPH}mL+2S=j1Hm{z!r01Pt_do@jeky z)&p0JD`nmXK3|1zM<0NV#{PlbR`B&6Xcd(NSB#q;#kgY1=jxpXSBy)(>;-Vq6c*2v z02fVD?}~h|Ztat0=K`Pb_KAhTVBI>4_tjHiBsLN`G6-I;ZP5{n!0UBlg^{)*#po^Qg-!FlbtI_PRbU3xYCQWW;<+?O_7U~uYP<>5Shf0?k` zwg>H4jql0_OT#i8VqPa$`?ntt`)3ZWH_yXP=LhqG=Jex`Rg}crCBY@wX9N1Z{AxRX z$?16K(fWKW^!r|q#QsFQJ+@lf#wrzyX)hhFed>!JX(T)mTAz>pn9C*74hQU3(sH@Q zA_C>aw_b4ND8t3;J`BGU4aLq5m2MVd6XZe_Mv~ zUY-&p)4t-@mBD@-FPzdiN2C^AdO836g3AMV4k01}GYfOVUFxt6As`cm{*9*5&Dfs+ z4=S6F|NJm}GqyhSLNUJ}7e$CAZJ6cUi<7P;<~*}&L~XC@>~22q#%u0ao-10?h_1=m zbljl5-_u0oHQRF+*2|bzlvylV zJdM>$SWfQ;*WEA^tD?;%goqdpMvok`&p{c4fCN98?bt$IdP!_%xb(G~E2qj&TYM&6+nkKRnAQf)~5W5J7mWl;z#`}{6gOG~t4 zaXo8U((zxup^m$^&tm&dA&OD6n<~)HVEuG#Le%TlJN}DR^rGa1S+~>tkA3H|A!se%SDtK0xABW_@>W!!3zUiEy%A zBuuA&B)khwKK9lrh9e|X6kl^H3*RQl=8#Iwe!r#Z*p2&xO)|@OJm2Hi=fgir@i1S= z*VShI_~52B5pd8AZy#jPZduRkLfA1;D+$wi{#Hf_mQdFT!#TL&m=3VuBEoFeRJ4Ty z+geh#)lfwc-ZO{Ry5;U%R(K>*OAf5V80ZTy#Xc2@H z7u@uZ)(sSjBFrY-!*v*Rfc0OAouC8uXIWhX9ne(1X#j-7k=JsfARLryEhZCw&jsacY!n@xNAlpW2{I5pEodVKwJyx&(?lQNZY#qUbL zY>+^2af*f1$t7w5_;DKcoG0}hyKvU9Z@N{2xORNVoc|>jSGn0~V%03P_Oz%4L@we8 z18Vcj_;-_z-?b4&bkVhuiRWEDZm||38j}xgm^j|IYcJ@?MFzIhk6s3eiwv7y>OMp+ zSl6da+@UiXnFJz+REFQlQh9o+l z^=zVC4q8nh*2N#Y+T9LxBX$D0o|+h+-+I0pbx;jw^2clku+pM&jp})NeT_zOF+4JNNU+dog7*IZ;URi_@T_lvtOYVf# zGLA~Ti9oH~)5jfySJmTEgRQF^23ru-CEp)df2AD1v$fu|md@u*L!>oxMnjvb={z8z zSUJ{e=0nDPNQqFc4HpmUsZ7+N<3De96*$%7w0ymHO&%SndCTt>J!jfpqjLR@Zb2uS zm^U^Yn@2CTx5=sZFYZS-2n9=7`|x<`ymks85W|2D66zICAdv#0Twj&8s|aXk;d_K? zDIXM&zIHzi>p8!e?SADu%9uDK93B*p_J)cm4vqbvA-!mAQaLu%hlb>)AXc-yA1CO5 z1OmB!hd)b~)}!K)(Lp1_x~x)EVbsA1!azR%!6K@z0?m;%&3;MGWxA?&g(T+&BWlj* z<)Ww#w2x4*_0=o(L{Sa@F(6Yyy_5(eI!h?m>lL?da>Z4ls|0FQZyx{hb$>m2!W|K? zit7J}l*ew2&;HSZ9>3IHy;t@B5Gm)h)D2j*#t@2?cI)B|a6TCj%5`ei=4;@L`tABd z30AYOq#{ee3B_|ug;}-kc4&ADt5(kg318_|D|)c<-%41`t|t^sSv+_SRb|qTO$YnDG z49T3;kD=?+5G&+@7(>@p3FPvVSV^%0kDuHv(*ZobASe6);B~?G(SHE1e{kgJ1-w3x zo7xO`{irKjA>jCCLc!jBsEzL&Yr^V;0W}fol|&fP5kk4X{&6mksm#D;1ZqXDKfF%y zQ5jw+LTx#2-qV9q-?Il@?rp={bfQoE%Kbk?3KRUphk zsze4%qFAoO~R0tH@2Y#EwtzB))d6*u3IH?%s2o)C6J3!Zq*y< z7I}pcZ1;({H$G&J@&Axm};0 zD)5cM(#CH-)!LuL(sKe>-Yl=ji~SAauFmj8znZ-*Bp%jcYp#{2U5x{=<`JR$D@JnA zD%M2n+m4s$+im4&SXdbn|1`&<{ck90+|GHg>p?TRw{XmQK_Z>)Oj?+ssa20|`3e-V zjl4lol}lVpd+SlZ273!N^b!rZy~|%DO_iZua(g*gedmGj_`$>Xg&}OdS@gUi6cV_Q z+bltRWi;JbfE81=>p^&2=}~AG6cn&On!4R2TLu0?*nFd+#1#me&-nayxQh;(KPFf% zUdwL`QnUAMWrrYT+YsmNwTz8!^k%6;D&~ClC}YP{ z$nf4zK@4VuaJzHwb!x!BH{)u4C;0aSzpp(9{=HFxDO#nP^4>VGk1wx@0lD}NVE}bSIc*UxqL>doS4f;EzE;@1(#13;d1j$A{g2JippJXjmc-*=*~nTPPKYg9*2L@2%H#@ZJa1E5w@rl`{p69y)%ZdgDtZ ztv-e2c-`ka&gN=HNcZeOVR-}^wXh2+Tpx|Q+NUsGoZ!D8%Qqz`R)pVBdm*($G67AngjdrMG_rHW)8Rnb>_c|h+ZwZ-#IVNxVgWG-2Z|^VQcAp$IVchQf z30QR4*6RR#;N_jY$ABlCB^*$ZaK>p)==H~yJxGuGjKv7&D{~Lc_EQ z6xl11{gR>wj0ekx7Q=wClz_$bV7)`&dMz{8Rs@^uW5NNaLE|aH2|Z-*F|OBX&bOO@ z$8nW{5^%}3iLGZ`va4&%7?XK%s#7t2<1gK-nrAH7FF2Az+ccPq_r#@y^3LN5M@$%>hjbXUrg+ z@MMPo#n|JgIp1*t9%J_$3kExVl2tWhr#EhzAp~}MRRR|OvE4`l^&9kEnNiDCgafW4 zpz$T)gyIPrO`v{XubRSqx&%BLOR_O)nMIefv<12B(bU1nha6*&CflbjK65A`y)RT*Nnm;#RqnUAZE zScf1FcRpFC+Yb1aq@4|`a5{<(Dk=c2S1aCkEZ})0&UJq;u#W8`iYC{ws?=L&(0*Rz zx~4Rxo}O6Oeix0B>o$<%$Y#)j7S}5-KEL}MAM;@SX`A4R)~;LP6L_iybMDf63{vd- zq`=I9H?26p8=I-@sYbp8WPWbAXA9EIhmcNwAJb1q=9VCN0xC^D&XfYFrtqg^baGxN zg2B~;>cJY+<;!mG;wt-aQ|I>Se)vN=HpI9cdjH?dKCnQra)P|8x6d|3f zjC;xi5*tw`0hPx;HV+8t{1x*cSC_K_#S>7OaMOoTIwfK9wxtaf_@if& z&DzN()T?@7@=Id?iW^Me0HMKEGye-L9Fi+pcf*1rmVnG2x6diCpvVHyNztU!SDjrQ z77lrYRE~wXae)x|n0ip!uCxN#%I{|u6uAoBPhmmf#HPyVfW7PV7DED}^J^cIm>4D? zljW_}bnYE3XY!Q`_{KLwEq*5qC$GBNl}bq+)(B=QsQfD2|u z%)ga@P80k0FlHMY*`=J%OhNk3^p(GNWa3Yk<5gyt9nY@)S~2q*Eu~)!RPeJxU>@((w!BuTD?Fk`X~O9S>I`o})&~B?2Rm%iQLX zd-3(?pZ$Y)7(%=DSGe1GmLZeGdq-PCo?;YJ)N)_tCbmmlyBmhmu-N!L`m`-Kx$STA z4n0fsd*_+TdEYBg>x&hKVMxuUx)U!Kw$~wP&rGe>v1j<&-dinv?j>l@)WAdWZyFB0 zVX!el+yOs6@UgjPWip;c7|t(i38i^Q_W#FtF1{Cjc{Aewi01K$9a(IfBJq`al$h@Q z8P!OO@$wFT#+sHf&!ZQ{AS)4PGb_0YOz{ZsGF}`~f?cGtH9XAA@n(WzCoTOCB?PD8 z8T?bMHb+%m(`jD`RwJm@z+OE{a-sl7^k29M1|{VsukC1yU<**25a5qZp&)z#W#KGi}tK9 z!|F>{4Z~9Dv3*r6ER{xCESV*g+UOLD^{g>qmQcS5s@c@dkL$HK;+#xdQLDBwxR%weq@b@qHc%)sI&I1fZOrcoWvmcZo4=_Mj zt;$Nw`1=LcO`+De=`l11;rBN;aw((KAwF)Ipqt=zF?9g&$Wj|{rv|Mf7we~)=TOji5&OJ!-uYC972dM#=V zbKh$(N;QZ3UL`e{SzHPc6f3DVkJ)TmZkzQH?t6VF#bH=X4iPkaRj3&5dn~c}Ljzp* z{inp>x~K4N2eoLhV3@2uUJVwE!p#SdgJ)wx`$IWIE;y^o8^N=&Cx7+{h-7f$zSUsC zuyW?O1`&)h*3dApY{>2V;SH9J&HQ?^!LspyBkU1aHm(r#I%COIW}Y~_Zt*6VC!U9D zQ#oISiwlXc392w#$00t7U|XBJ7Ke$PS9-qxtq+uqM9jS3j@p3w4#3$ zOe|{-jdrLFx1rcK`Mj+!s!`e&#pTBM8{*IoDf$?ii9g)uZ+GvjMlPI>ECuRw>DnwW zs|5pHXin#^#q@d}jTqi+_=B3!Vu@kP=P%08?!dL;gYMta&qgj)edBo4PtfZ_yU`+; zh|0SRn2E^zFdsD&cDuq6z+&T3(nfb?_iHMBhp}LkI2&ez1*4mw*;nVaWMCrVIkILM zST=^r&6$bl(#SDekAZruXcpz~GSZCr%8O<^b#6x&ouj(!)>I+Oug+nM==J`7!`+b` z+O0^sdvld_(?zs#5%qKOp+GZAvhZA2$84yyHrydnEDOK!UylsfiZW#pZ;JLIT3zx6cXx{8okJBGEc&^{pf=d5f@W_$> zg;`hk#p|(Up;M!Gq!i5u@TZgKy>E8ZpyYL|U^>BpMqBM9Y`xGkeJ;zRqP;k!aN*My z!E*GDpjTO|VP>Rz@!o(1Mmj2VMbRLv(E=n^GAMS@I=%z2M$2sSW@b8>2!3WORz%Qj zkyJOcMl02S&dhWs!YT9`Ee(k+VcFfI+l-xeUidxP-j9=6!uDE?^<$T7At$Q8q~m(| z8^;wnd$DGgZ`+{0tPzVl#ujt#>crhFX0PYQe8VSOYk&F0 z_Tba)$L1?9Z^uOhz3SgOt@UeSZ+5HG~4jzcO1-&TOHPB73muy$21> zUCsEVV3Om>w0^8PZ+-9U)rEM>$hfXMp$jMRy~umMlP)M@EhywW)sG7m22z4lZIorj_HiIC3uNs69IcWiO^2J7{E1jQEq z!o0A4e|)de3x?9gR2j2ApYWSPvt`a5Ghir9o~b(zhSJ>!{1g72`y08JJUQ55Z z4NwkyO>7Fa8Yk@3g>G1Pf?_K($CxtNsI#+nLYb^**Xk^PWPr!POtlp;@(+kk+&A)Oj_iRt<@#SuBlWzfIJLKo#reH<~(7 z#cCoFEdbT3`FTDcpjtICP1hT$RXdedUw~@WR&g&UsA9DysP;&va4i%N=@Qi1-F%BF zAo3?DcCqpDJSZ#*ZU5~9g+-&(m=jY#bgu6U_gT7t=(3nZ2ow;l3_tk;3Wzk*yqU%# zd79M%s!hM7YYtxUJp{G7wY*}y;L|AfU#|k=9TzA(E)U-Ea~yVz7yOEF8{-96QH)}| z;J!zGSArLuGIGdf%;VFnR(4NG7u;U-BaF=L_4n@f&2W27qgdTI73Q|9%&I4ymHn2n9Hm0S_5-=DJAgO!)-T>YIhmkIGQIAg6>U`R=WRJ_Nd#UgfBFU zokFpXPWTmy7t@kS^!LZ#;+c5umSz8Zt*ViZg3Z6H^;vkQpWzz)mU6UxlQ3sSem3^F zWPI=rU;cln)=I-rDcD*pccMi*70q61@FstuFESEcbGSb99g-%f)_j@y!MAfCVGe>?YZE0t zO+0`3ABz2my61~sdV{tYi7SlFh{6ZOUmSYA%@O;&vea{W9)j9dJkOgw?hb&)^Ci@J5RWYOU;P5j#BF|34I)*|~NDQh0*LxADZqzIMib z+`eIXn*uQJjW_#!T+X6H;THU6`j_bH)#Ith0J$}<^P`FM_XPu?S*q;;vcK;#&52O?^AN2*X`SgO-Q-C z=t4DGG27(wwUhubSDn_ABFJZ?=rhf0n?&4T!yKSf2fLHzEn zZkrzD^*mi`1fYuL`^Ic;7_o#3j$8wGn_6p{0WhKZAnj`zYy#`#0= zJ#4=p?>xecSV89Nm=P<@Q-i&Tp0KhakL3VEnmatd4@R<-1Xn4@j3`le{S%nb$oI4R zp|e3s5F`W*azw4QhNu=HVUg&VRG`Vz>7@Zj@kMv+NBfURc;vh4k7;hvD zA(QY!^EW}r#AG;}>5A***=PY>aq>!^*q|$p`{iP$D^4@O;0$!jQM2mZjG$a@Ozns` zl*Og0zW)GaaW9qZlb|f_oBQN?D2t1%Gi1u;S`v0qfP%s-8j6|38aYb%XxbEjU2y z`Bof;q!>Bx&=$kU`LKA+VvzHkkIWhUKUZ!$ftZGJxIpJQh!QLTVgCPIe$K@N-+*^n<@E7vJxB2 zby06_EyI@%yIs*}s=x{_a{A>|Yq57!Q`VW9MBE&8U6Usg;}|3N>wt7@Bu;L$Ydk=I zmM6~$P&C8ox+qz7yoN51#D^1s+4wJsdoV*G6AdnzcV&&a3)UIBxBoVkj_mof{`Koe z-~~3;9eWl9*>k6e1AIQ_uj9(V=c92Xa5wmTtknMGf(_?kw~rl!$$n@~v0vxKR_`Ee z_JFY7M{+jb!G@zs*zOSTR>p=?=CjWjY&cd~nv4y{$Vq_j9&N)}74%LT!e|z9?i6E_ z;ZR!2WWA=@Z`rnUS&#)2C9Jnmg}VS;GSh6g=7uEWl39Imi66LRIIvDGxMY+#c-g=u zv+CA_IAq0cTQ)N$ma4$(%vUwdes2wEe}J!U@DDY`dTYIuy1~UV&32tlP%F4tPW=$S z4K5aoDbV=_6y{p}if!Kc};s^W?`FW~_wu4*h=4ctEDvZt9FibBNo&F#PrgJRl7_HbUGUwcX2J4IYqo=M_wG8I>?U zV*(P8#YTFwA!szse!m?#@B@NKnS}Kgjwg45MZ=4*-9Lwt7>h=j<SI=Lz-Ee8MJW5Rxgu8uvTb3zmfAgnj$>Dn5wP{R zzTlL>VH)kOZhjrTW+_#L%wEOZ1&6~YH<5?naHtaIu>^;M-mv2b$oc$Pxz!O22#d}5 zAA$jao6H`HLv&GISvJS~46IJ#V(?!a?zauwSneGya~U4~lrZDb~BbA)pe@iAchB*Ko() zgL6Vcq}UeDiPX3h6F4W7gN--`=yRfC^UW4GCxkCMh+4^HV2iZ55<5g{usvbFo||WA zQE{Rrn9XX6^_qMB6MA5L5*;9Hci9znmQmd_JgV#bYT!aL-lTKkiFzRIVqKDbVYy5e z7Vg_toFH9+-4+(~%_#j$D;-V#F*;;!OxQ0vblT~VZw;=RV!hbb{i*rjrV`9`cna;t zB|oONsf$(O1oN_CqdA4R(y3hi)<_MV0n;3FFe}C7x-}XHUN+$DI^I2zS!uYZ-l77I z<^zQNI`wUjgri%SuwE_QCGt1^)ZkEt?Y3g!nJ2h7Qfsi?Pbc=^yXADaP376y2lY62 z=t-g9_DY;)4gUG>2K-%;azM*vB*& zZy~JLlIzbUXgm%gZ1?x&dxcP7Y&l-L9}0~5lpSV5f$_}tT}JFC%6>sgWg}s^*%m1w z(A%L-Sns8!E7YN;{YUJyTq_10lq1Fu%-0kKK2W>PS_4XKPs==~^s zC~yu^Fvj7^n;`|$sogCNDHs)><++f8@g*#{{%dYjc}+E<2nls3?00z26e;y2te5B0 z(_lrfY%ELAuGVG+u0NI8*rYxA`m2Igf|$($ z!g75DQyzknQ75eTic(@YD4m8sRo@|I^YNlt2`HP~h1|y>X5$<16hX{pp^*d=v#A?< zr9{VU5DQ_!%c(34_pbkwP)dTZ-zx-_z9X!6u>z}t?v@<%gRtG*bHcV(-Yr8D9Wz!~ zOn$`TCxez*g*KpTdG!=Kt2>QSr$}n5&ucrd-J}wfYi)3a4dQ(khxr)PTP|hr3G#md zWfkXWn+}DZUF$4?ykC2#Dr38m*!r;qY&VAn)EL`MhPEvSZtpyGBV_)dco&A>RouDB+zP`u!H7i9wnXpRe!duJ# zqtwisJENhq;SyoFS0#pCLv_ko!g`lQScO2xL&~chrsIL5`Q8#}MzPM3Qh~|__3}a{ zC*(iVnaK(53;V%TJS-(F*z8Qc)D81*QzVq4QD_Qbzt0IOEhDU#<=zz^Bl^dgNZ78j zWAQ%j`_Itzi+9ZfuKmC(w32Hcah2fTM*c_S*avYqA*o@H9vDCnX&U7&J|VgT)^NcD z^@>R9o`tntvhPO+3ifuaDYnd6y;VY-S;J}9>o99LUpWhA4dFP(#hVS{xl?=l(n1RK+s8-u;AjOk$bg@1MrC#)D#L$AnaG3pwhL3_3HOm zjc;b_#^HqRhT3=73%B>+9^rov2gl#x<{6R&7Ob?TGCyLzDBX%ZO;US%FTH~#RTV+G zdt_esLy~F_LB0FNH>N<6s)zE7W1>{wwhqcbl&bl=&T@!SDN23W4^gV~drwY6lxj<$ zz86HPRuC4PD;y^S49A3^Rz1Rg)xD=stR-Q+t{g#)lQhDqCTRCp((WWcI6a)_!U5q> z3fHe!0>ingb-xrCPBvk+M+tiUoq1;!JQy_0VLb*949zACr=FnLLc(}5t>;~Y_k!#R zqVXkYR(DJ0B)l*5Y_kCKz7YTV4b1yOXWbd{N+Bn^zX#zhmbp&Hn73ORJdZGsL`|bu zir?ZNJRW#{db zq2lpHz%Ql~^891lXlPPj!k}2{2U08di-W3lMfD7(qV$mA0cSA^-91_=!E25 zEVvpvAqCAeraB=Bdi}Dnj0q_CJA8md0}9qY#snHzzE7c81qoiJ^}@&T025FMi^={8 zB^Qo>W+}R-<4%kG21qp2EV|1i8cz2s3qYb_f}mIP?dM;?Wzr$e8UvS4ra<8_Si>0- z6nnD1jak!qMTRkVNx|6-3t=rcjb^_k9_56~XEcw~9WI|ki3Zo<@)4%VD+TB^f_wjy z^Uu<-{mvx|!g6EKtW75~%YVGa-UP+=33?Pc2W8>&M}sHD1nW_J(sIOkFBwY_G`s!v zn><0~EIcClWoM0aDGKb%&u8Vx!FvdL^?mDo`~b}l`mK@=E&7{@SyL4jmI+s2$z$9U z!(#8=da$Nj6XVrd8TUL5(^12zgEPdN3b7zTvo}g?J8eqnR+|G)G!m@>&{kEAQuf3$ zIwd!SUW@%xJwCKvM~2ELKABCFVEa~=R2^Bm%88)ZmAvkLx*~6|$AJZDYn+4dr?6C? zRo)diNQ|0dv&P=>Ybxr4aOGPw|6i?i2~}8|lJQtIt|RDmkxlaFmC~=!-T6%yIN}R1 zO`xlnEbYco1jUxzj1Zfxc^_HZg;Ptl?e(zS(D8Kg-vpGV<0H3MbK-=)4QF3XcEu- z3WH$MG$}P8C{}`y)N6Z98AKC7Na!qrW_PP@VuDF?O)oG==i9^}jdW=KS5MIEFt@)J z)a6Rgk9C52-Dy-hNKkAWL8(3Bp|;SL+e}Dk6hX7|l%Ab}dR^s}7EHZvNW^gyXxCLE z=ylS-A{a`kwZ~W|pv0OXr4)@yw-OdxK~So!NtgwcQcsi6Me0*%c1MM@2((!5-CM%6 zSPSW&(}EW3T7q8B-Ch|0p;ND!R+2`TjSo0+zG6h^;ivH8%oSvaHjcL}s+)e%yBdpA_FWA6l&>N_MV=4MiA1 zr3YsSF{RR92}(_u3T9fZMhFRw;kRNI6z5gaH^PE~bFiOTP{il^CcuJXmz@ODlF_wk zW;L{A*xtDsO1ElIXd6MLE_V@At8s^*RAKFROijiSLPF>N8dru6=(V?A4MMHPfdB`l zRzpQu<2;m3Ww$a~}MLPAGk=?aX^Vb(8FA&}<&*@{nmlpLKMKlrD;r zcnt3WeI+FI`IfH|P}!72Na%jE&Jk!r|ARL$l}(jJV%p^t-PyzzpaxawD9(Ep({8t$ z$D$o7m9qDr(}O6ruM2}p5!d=TOgml#L8&bXzWtDlzC}o=(%40&{Vs2p;shkA?b3cS zNoo&kmwS+;7W}ER1^k4U?yERK;Ss-lV;%)m>J>qyoyMaRK&4!Qrcmmo2bBzDI!s9D z^5wmZ;qVFjLRNrMZcE$dL*tQNr`$h)Qn!<5^g?S!eMuu9xZ6LcIDXHg&F$=jl$yS) z{|PM`s|iUpbNE7~g1h|}pir8Ga)dM_L9K?Rtxq+$+Z#nCiydiqyX4|$w%~5}+_SwC zRy2V|Z&l%(cx1bk1I`KU<5Q$GGiS3UtZaS~lKNO~0~?$Z^@M~j+`gp{Ryf~g%~gVP zBD<3+KL7_sKC8A8926_HozF%XWMW~i0qs>IbemO+l651UV0uBLQXr*X8ds;6sxn{hKXB&gK1cGhY553naFb(LlEX)V4|+(Jm`V-xPj@Go%ZHr6?G zw;q2xA6hr(B>j%MA%7NEWIdMW^*j!Lfv5UU-@wvpl3Q*~zEwNEMM$ZQT0#f>1%DEf zN(I(zy$V~aii9Z=dT>u?8T^SnycW)bE!T!?LlUs%;*&bG1GZc)n~YfN=>LaX4y!zZ zCX~&=4a3l!VoOM=*+QWX_znCdBz4!U8h2O&R}2G=hqB=q#~ z0X7IQRzEq-1Q=h(*xdxYe=FSdKH&X-Y1Vnw3Nl>X=yMhpi zO&fwzHIFQR2ja1jkkD5R!TcaL^?EllVl!eHw;_m4FWYc?5S!l~^C-hB7hWIvOyHG^ zHN7>L;8l#{gp@i8?6!tiF?4~XQZ!2amc;oLUb%QcNT}hyN?mvrgA!_YgI6)Cozqm{ zRgC?A|1qy(c=srd!D~3f$48mha27vX+zGGe2oX{`JjSyJUeC!PBz5_XHDmA^&L=`b zPesbw!s|KG9mWqScs{2rW2ZGdpL60y*&=v8XN-G_TuK(qXPV1iJeA6W<}#%_ug^ns z*#$x}`BO|2pt!8*{?2|VE{pcnXNt=-f@!izgH2YY(dSTH#*tuB0L5jI&ZGvaQZ?oP}1bYagPSR_xVr z_9;-KRgt{G2WqtJ9v3Z#7A-6gQ2;Gk$pl=cnu^y$;qd65b(K&!EPP=e(>MHufK2xz zlyDk!yQ4;FJ189P{_MhZvx^e4dHo<4Q#j0jxkefahv%w#Y=FYy^eaP*NhRUBtx z)IFSg7+fv#JS$m(==|V#zn3d0E1znXG{_ekJY=;1lgf*U)F#Lin)7!srj`^cx_%N| zEv_LaB*4{jT}y>=wNw&v8OJ}71dYZ%dm8r8b!k?uo;!8$P^m;NN=!(LgPr0eoZJezMs)uzR{irnc~|GmKeUJ>oAuI ztIRmxjOyy=l!DgND5<=vok;hB{V2=_EjYe(?G3xd^+<7METjEyepXy+);Mu4!>}4z)r#7(kM@F^dP&W5{ydtgW zM!T?Ss9p`C?hI?qcA^7HlFD0TS$lC+w${sST@6Sly!}FN-WMG9ReK zWWGOWYQK_xu19Xjk{>FWef91##bh@L*t|DBmnkx%?p|h!%u){RWh$9JT+r=(MhX7H z;X>Y-LGGETzdf>Hy-_>X@viVR{MUjw=N@k{kR8Dv)D#cfrBktTG5fXfN@Hm37=F{_0+6q^j^ z^bJ9=38FzJ1tin-sHzk+o9y4SoaubVF8`ROl9_~TidsC^gk}@g0g1=Rqo?$1$f;ojVck3E*yOOXoI?VFFs{sbK5Yk9glb9zYyP9inm#& z4F?OgVs@^9d7<$wcsAeVl!rYX*m+euuVh~d4ja+6arNuKp*CB0{JcUx>7wR#$m@Sh zorgbHfBVNNqDVW@9#R^TR46AZJEc-IL`et@LR3ekGLnXn$f}4$LQUXZ|^ZtDA-#>8Qk4L`V&g*%-hIltKM8Kt6LD&r_t+w|T|1HdxR!i>9#?tC~nvl$c zAHu#tYxP^FWJzeP&Wijc1+CSm3EAxTx`(aR>tZ8=w=2d{vL%_UFT9nS(DGw8c*8;> zg!h_1VB-2+g=5L!lP~ND)5SRM(^$sVgW^(OQ1dg3aum85Kz#v^(xQ6JSJ}c z=nD{vlIL630-?C|tC~Y72)TSwy?!_3f<4L`ev}t`GcUB&S3@p%%qWLsnr+e!gk12i z-D5-`7yNu>H0FX2DsjN3)B`=t1&{HRnJU5Nf+t0&LN1u?|8KiH3&!G)5zZGx8D534 zcrFLDl)s<~2`B9Ty%yVTPZG{&YwfWH+HJWUkiYmiw&A|oaAO&?;bzyl6+*e~ zy@2C;me6dwZS>{s*lsJfJ`US$L!&4|Xv2L*K;aABjs;-Hzd$&jkms6Ku;XVC4mcsF z4DI_>PbDXTeSh5CgJ{QRzWhcz{_cyyXve?TC9@gq_+?9C(7vC~azYwFVUx2ezWqal z^SR5fae}v>%K<0e8!U%E7MBz|7Qo+%c0X-=`cmX@wLQ^BwefDRNjO3pgN+ATW#Xr|VeWm(;uHd5_(Xg)+rmcN#lay<`k+ zb)QU6%V8?Qqg&K0ODNON9y~1KRmicTcNi-l1$2-+5NO&tlG8os+K-U&aKdBsDGVD`Fo1rW3E3t*vKtRCyF~PlQtEj+A~>8!lyH zB;2kqm=wyqC7|$_&&Q0tCqmd74G!nqQy(%O-VOF|HgdqvORhYGcVdP0P?6cB7Dm(X z^h9_UjwxsyoR#^RX$#R7D(ra4tUK9pWYNYdW_nMDU-_d~%paP7!b5qJw!uZ>D&c(7 zFMGOx8NGyXKviLf4lti@n68KiiGyzj`@w9keNpiY93-wMwc9yK5-es-0&j(1kCy51&Lsz(q6h2bHLMb zCtwbE@Z?pD?PRNKl}$-vfB%24sKA_$|Ld!WRNhv^p!qDgd+}^QDhppvxBx}uN0MAL z0Kv>1H`NaiOk|0fFzkZ$g-=7IqPONSB9-hUb0jJGo_rHe$&fv#QU9HoQGu z2QZS!(|#Nynck<4m(vijEHquV2O^dl^WS2`a{X&DF^E`BiIBudX7oG;+wVTxC#?dp z`Zhz}4tza-)EC%*Sj~BSWg_@~ZfQhs0(JXTDd7{STa{g|-XK=DtA^|Xv3k63xDLeX zx$6cQAXXpD^QWyr-LC1HsR}-ySFh$R2B%VD$f>2^R9c&=dl*ux7D^v>fc&mDS#1uX z)EwVzQ7_i0bU45`9zv+Oi;6~nvPPxHlP3p*QEAy&aX&CBy%RAw2S%l;)0M?wR0{Bu z{0s$zGnubp?rZ^J#x|P6?KaNs>VpEpI>PC!Hx%+f0U;O5N!v*dKmlRcU>_C`eqwB~ zfNb9x$ZwDz}NfG`5bPuX$ODV z3FWiugxhuU^lyT4+6x5J71ymafszU?mfOD}pax1OZkCSt!8BX^-n`#1&9+YB7nW4) z^y*v+WwcgivvJ;MV7Fx>yO>P>$L*HJR4sw&R4%78xHZQe_JFupZlj>yFPKg{t~&~*kg*88+ZDWkUWKWyjsS4e(_$h^;l&c+Uc>0V!M_yf7&MFh)L1dqpEFfBDb z+y#>xIq?H>z;lKYFgHBr{l48$(P)2A7m-bK_XbZI+->&>w|jEXRT|uH%L%9Jwf~BY zN04B-BQl$=v&@FNqHJ>>kd0bp{0Ow&itfGT0*q(cU7y|Hb}M%>V6D|{OvXRqII!nE zA=vI(SKLRi?^O~^7jq$xjsZ#~Pq^H?+9pwePI}?lXss5$zj-@Ar}IZN5mH^+Er)hJ zUd`c^@DN#?igSR6NSQ4_kL5_nRkeI1@ObB2P9tCjFQWApn=Yw9&3dU|YIc;VRBFLe~a)JmcA&i2lT3G=xGhbB31``OaZ+rT`>9r zs8(fME}~k^hx2YgrNY#T85mv@kmF#xcliq^L3r&F!F0h6GcQ2e&HFD0%Pnflz($23 z|LD1Fc#Wn$j^++Qdd*&;;UT8o-1{)?c2VZd7m!NZ^1Vl-u}F* zRfMfKL+2e6U>lB0!OmE}3We^t*#yIh_Q+_!Vgf}l-AMT}0a!@jv2)RY?l{NR@%*re zFnv;hICRHdcv16!hVnR{ie@a2`#6$W3*~VMgyZoHJbVc?Sw4hfH4$z%g;1^)r#YC; zr*vWu)N1h(EXP;j(*P}5!7HM3pd~BY;kz}oWIg6}424!LE(J>!kfv9_#Ny4P91NFw z&T|t?vCko#?nTvEoL;YdvwSg3EKY0acn(wR*Xqr1VsZD;PMlc0yt)%778hh)PKJra zK7#Sa-l>}bhK54~Vr3F+S4JQgUoXw!bT6iDc?b@OBZSMj&x%J!Lt`8dIvPIhFGELz z-|NljfKVV5tVZud9E?tv5Da%j<5oO;Mb99ZF1)$&5`1P$I(S*a==6;Q@l^P#{?caD z3!Bs4bX>11X!tLw`4%q4>5#_zOEJfId&9i5Zh`|?O7W2@??rstGTDM-e5?wt352`3bcE*zX94Tga6 z1jB_XPs421N5bhIU7YE!=U+|T<;g<>fsV#bCx~k8-=K$n0Hb`jt>6bJHu#5rfZV3R z*&A8^Krg{~*R^vkOJ)BDv1|#pQ{zA`n(8B*?#EjkdgGY@(h-2p$2o^3-VR}N5Q^)TC)^Zv)>X7_ql5;w&xZTOs9E#j-S^?)&SsmI@53UoMkjBa%?>3T@8A99+s0h$W!4djl>pq1 z#diA$2is?nt7d-~GHg<^bPd*kNoRNx{~IyG1yE+XGOBemeb!Mn1H#@+AnFX*js;ZO}3xe2@9 zzR}xp_xm;Bc*csOMIgUx2*s)j;b6NmLb(PBrt`6SPz>_>G{JHfwC4ek-^2P}QGT!4 zwZItU_uIm8_d$MZAE7xI?9{dzYgoZBCK%53@nzQN?@O&BobK0@`SGXO6^y0IFO~1mmq8ygv&fom@)xvzdeK zB58ur+z6*L3Ld)*GM(!QmNS0QrVg1-jac)n18k;q{*64$bSBPgZGlW@B*AR{gqjWI z+|T+w+(^Y-O;~g8SUNi-$&vTR2}N@w`k$v|`Sc}Y(kPm%->(p?#$z}wUtG6|s@!1P zAi6A4ivvjsXfLH6w{Q%*}isIC_YWF*^$4Zu27e&*`tE> zx~+-+II+Y_Fq(UDSpZBh*{k?3fwtU!tFIL>x%8f3H3O?HMNqFhPH}he61H8pHSrv_ z>k1HRW__nu0gTOVH@#UWH0%EfRFc3_2tk6;?r#6n4y6&XxpS|9v)SP82keO8C0Ol= zs3JO{`R0W$1}F5`jS}dDzDcOrT|0rP@DzJ4QoyJC{j)keKnM|v_UA!5yJe6~y*D`A zxszt!V#&H?c#)_hSS{)HMSQ!RW9Rq5+ilS_9pCPq1e^UDl9_v4qnwcrf3VWDxtBSk zllsM6D~tL-C|XeVvexwV(TvonTHC1^^-Q91YyLaO$5bYvYMYH>HCGtrFp7s{%V_~A z_IUItNzuS7jgcqVOh0DmWa)4-v&eIHx9zMxRz?f>>6d4AY6(UwT=5if`(5L4GXb}sT(ccs#xJr^NgNY038l-6*<`9;7+ib!2BBQa5x|$ zj2#aC&wgWv!xMtda_2{#gumz1hN$=O_n%9-atTH2B^2z`_DR<8=bQ_%R0&m!N|EV< zzyB8W^$oM(@BiQBPPXv(-;!XnXNScxpNwX`FaBQ0Htph}a3%Q&LMcc`tVC-_y z&#&@8IJgumm0-1un6%?SMADwJB{eJ&IjTDkiAWY;GnSg2P3Y}{4F}6h%{{RBU_vOD zA)#p32nCz*`VtRJIK&W$)k3IRY~yLC^o?{T^Cara71(@OGSs7+!)`tp5o~7lrt~1B z)fn;SUP!m)63VrUV6-$s!OEUVj6hgThCnP=7OMeO3sbhhaGUyw*C2%320oc>hj3db zp=N<%4LVC`Ry<12Ofvzo$)#ME2}XNIDA>yL^Tj|saw*nIg4OJ&xueNMt)?a%#HMi5 zbrhRR2{o&W{)p9XbN*f23Ds_^xsZ!cv@5A!!)U-@UK@M2LATpgLa}xdtd{Nf?mcw7 zCCqaTfo?YwdAn24?KUYE}ItrCF6i?(E<|`Y&T|^*P%Zk;G)#-!OOPYhhsKv+M2ReB6 zQJI8dtv?{K$Xx0trL7=b{-?f#y7<_&IEm*wrMqdS$(XKMYIb>H>KUU>YVWL*zQ30@ zQ`4vJy4*>%GvS1Cxe300^d*e_GfXI0+%L(CmELT*1%X&|WF4bc&1|FS0~UWizjAG& zj5K*Cd|(d-=c6xl=PR(k?{AYDqgtAoX@d)zrZ$vOj<;$(j|Z`%(=i;%bwBc8`@FQz z%nm}q0z;bq4c_Zx(g?&-6V|jBGHPSwo+;(+RQ*ITOSWwE_2^?n{I6*7j?JcG;%2C` zTd_0~FlP`)r>%@5V_|gKg;1`i^A_OPtj7rs1zXuO@du1fSFs=7K(QM4JK~sZy^Ezc z43oTmzpw>HrxnC>Q(<&^rhg~a^C@L~%Y?{gb*FtBL_A*+$h9CmfvxwWTbVyB1_KJ# zsyY{c#H#aC(hh#5YeCr@IGzMg0DfP_9&mYAlKTL@1bOLMY<&Erepd zs(6?H`2FL`%n`uvOVag%0Kfk=oDl%{J?Tnp&t%qBE3>n&41&v!W8<+AVGn^^N2ZI= z*P#+Y#*jn79!zfk3k?T53B?*z+mi;32$OVI--e`f^uqGXkaWJk_WmA7I(LV>K8iJ) z+YVv(yH}t0F^Fo{6Uz1UoYVoR>9imeEbDUQFhsA_3B)ScL96PDB?VDwmd+Va7Qg1K zQZ8h*zqHsnLst9V$M5No)7d?g#S4o^TlgQ{gIrGzpQ<&+rkQh;B zXFA#6e?q0w)f+azPG?5#_Hfwg%sKPM0(Lq(-RdzT!r$nUzc-9wk}p^oK)+A>rIJvn zQg@7Kaf5!JhAG2Xwf^I-$ziDLu@7W)pjq8CSaUhl`*iO(kq-4f*?jZ=K)ug~=_>>C z#pwrBD}he7-=yd*wM|T@Q-)-jU;!jGfyd5bAgN)WQCTwagrkfs&Jg)y(Sr zNqVzh1~E$rl=3uE^AzoDWTZdxYN|aeWYPzW=NjK+OKW=$T!-Vfc|=|m9Je2*rv977 zUc(<}SFGuz+0@IM7mmVk4*x1TDh9EbjTK*x!gbrp@gb&QHs~}qz%_i~*k^bR|MFWH zui-zwbmKLgpFpR(i{(tZl!M+1><^0Csg3m?opVl-}3lnB9yXqGBn+IPH*^nyPy&UeIPnyg zatPazZaf`!3+WMm-pjByy`F7+#mxS{ z|MmuL`^ogouC*>;&FW{Q%QG!=dKr1AzvbQT>D0WYM%zZwA57D8>jS#wY)Ji^^o{dU zeN3pJ|7V%1hs+s~4X=)09b)LRf$`s&wdvFk0-b2PI0uN=r@Hj`LmXdS&iM+&@%Ir3 z_3o`3#_{pB5q%jac!ydW+^mE2muv(dmaZ5@b9 ziF{2Im$qqJE`zw-PN375)h7|xKdGE9oB8T88)CC_1-zd|Ak@pdx`_9u)=zZ&d5peI zDd*fqUN3QrL#6glyZj9J{=40wuYm7QTl!m^Kc$6wxASd2^n9fqDc=rZvp?y&41~?Z z{#7s#HjA~rI|q8dTGE}KLD)i3gt>tLJ_T{7i`uM3WvGaFc$^SHdua;rRRt zt-ZB7jcGd@vqeFql=}5_oT&jjo9#38&6kv2pD3S77deTd9LiPCV7c$DCU&@}WvZCX z{}_+xG&@|$b|HwShBxpJtYmW$PW19WxT-CGxh)b_wI7^Rqj4ttx^V^0Wb-XKh%?## ze#JPGZ8B<52(#J)gz?mCieazkZGvd}EEYb4Ue5r6aO8AnVGrnGbN*oH0d+0kiM5`) zE{*#HwVrQsHQS-qbM2Evto4*wOz(RIMW9^9lO?EzCQ^QX4xLwauSLq713+6!(u52jNt;yKg&z7<~1odnU; z(MuPMfv1zpa894h*$q$aZN;DX)C#BWz^Bu%E&!j-yHh4zg{Sjg=yywaIlGg#;8V+G zJO{eEnv0?{8ApO>7QJ8h72XXl!r82@jqgNMsjIq6YbHf+$`QkdVOQY>f1wdSMs{7K z)`?Bc%q*(@aP4;15wWsLqam*9I~Y*7&&ogz+;+=RdH#pw&w z;qN>3GC~~wzF+x~T?XF+M<4kZ!S_JshAov~HaA&*UFnx%6Vs|DwG~=3#$EZwfY;pn zOd7Uo)Gw*5si9eS`VQe$SffGBn1yC@CTSg-&F#Z3tOm2WL-1xRFsJk6n5-3<)x*qh zzkrhqA9{S=!FKGpBC+EzxsVjM>^_XkzGV^ z)lk8_Xh@`khRKDo3T7$L*}UZBSnO=Rc4kL7q}SG7Y>I}SuX(c_oFTn7EAx^W^nf+x zd$vK^O*D3CIi%g(uU2Dt&HC%cD-d2=7i5qF;Wf=NHAe`qy&jBifwbG&-p|s|`^B3w zpaVc+T-r+d8`y5_f8Iu4&*JV&(f}laQ!b+KXZ>nrBp4EhdtZUjh+b%&4nU$qPAnLJ zgovrHH2?{tKOeUPz*t(;(*-u1VE+SnzLzsm(gxpFeZtteuuuIj;Gcb^GR)> z6}91h@1{K$ok}ieYVm#_ta}2HipI*(ZpiJ3Jd6+p1fwaMiAEyVAnzX_#SVR2jLxS| z(Hdy2s(z4w&ZzF)yHUEm;@yPOZB=9^O0nLB<(ol@m95?N6PQo|EnA3C$Te~SYB#-u z71(%`x#K1-nQotVzW}^jlL>NJar*lZc)L0YVtG3DXAe-YWP(%-@9#MeL#7hlAvk2Z zK>6A}fLd{?SxCW@ZD#+2A=8}<^9w?#bnv;esnFNQMK0O$w_Tvm?>#{*_f{xOn5e+G%x}RNI7V}7!WlUjo#y2m z_J2INpQ><*3)(oC@*i>;oICB!Jk=Vii6EBn!y$jRj?bXDMCHdowBkYjQYvjx;0nF_ z^bacj`p40O6JJw{|7n`^zxYYjd&K3!Q4`hLJ+{%ddw?pKw_Iu}#WtnPHT@gJJ4DrpAGy-8 zx``^(0wcA@)f5-0=*D=DY1r_I zIy32pNWXR{BWgO)((8O9^;3S!g&u)8%JKV}cSproWAnyWp;LOAnc0>UJ-dv!QokhJA z+8+H&sh0_Opu5X5@-5YVDj63M#%e_Lz&Py!f?S5jSKutS6e5;Z##8)WEG#HA5u&og zUJ@4(mL0oBPlsLZ(_ZO=Fp01w(+4LM7G3`o32U{D&OX=yDWIgP3LTIm1i5IQ8O07r zfiMnY@sN`J0v(VHAQhIVsO|~F4oJm`7Hu%;z;ryo4#;J8N3a8OajW!g2tL0Wq!s>v zW%BE{us-;;G9bvM<@_%k5KbY8<&l;23s|v8CrG7W+b$fXFnUoa0HYMAbkfl=d1G=^ zJ&af+yr@OTWLSWlF1&Q+;fp)qp-W_2he9O!sf4&@*h$(^>iY|>`KK`h^;#IY0FQinrS1}09u1j8o7;}oQ!;#uq5vPP(Q zj@h%z50+2@7#`?^rm4EK%2Q!z?7;o6x)3N(D&`;;z1d|CAy9CRAQrWT**Hw5F^hv# z+SiXAf}t@f#-;;?#@u&zYrxQ$cxIFh1Q1eqlfqz_Y;Dg=NBD}?59<+tujnUY9O4q- zmd$=ev-!VFf>^@OF1iKNYSwKWq_Se16@Esi4gB5*pV5((7jY>q!~N(Ge0KkMe+Q@4 z*50cagsnF&fEcUIK`z|{P{gd`5ElESF}Mx)gAkQWfB6RmG`sbt;+6Xvw%!(0FU^3h zH-7~)T^NVs5{xkihLd3BawkD3f9gFh3#c_Slc{rVeYU9zbPRxaOrjHF!L z-pPF5Qn*r{UAgp#4#bhmv2%lPS(AyEyv%rQdrADX*FSu$9V!UUYmH`oVuE2$rT)e zqQ5Mr8gzza7C()MrTNrzcMu|v11@+Oud1cm&tx12;n8l@F!7JxC+d3OiKHKC&5Ysh zkovg`;;4Yl8pG3$R5QT@!JHhGUj>&?E`XRwkjozeC|(f4!gD?lFQ+RAQ8_e)mcUb} z>EB;va0%UN@=^gVp?;!Oh z)8!;mO-)hpa)7>{2L!>Cwx){)ZT}yD&?LyEjsS`}LRfY-d^FOU+)Nd6hziXtSVI+; zj%lXyCH9Fst^UERNU;%9^sb{ehq=A{AoV|jF>tR*9;R8u2trwR^7JQ|W@#XZWx?Bh z=V8jlb^ouQjF6qHCyjVz%GyhLAq7)Q0E8w%F0%=suqT8i@vnU$q+ncGqCx{wk*X?n zffS6~?spWVV5$$*7DEc=NZZ*Pkb*f&2nO|CtQ_F+TY^x2jMw20W7%;qK`eZBcFO@a zN85TU18k1In^^?xxK6Is1=w-m`t>7)EIST;FrU}5mklsUD$>)MSlFye5RAgp@tdGp zol7|U334eX1SEza7Og8g*F&}XK7v#puBq0BYV|)yPrZX`^|a0mqaL7n;@g z5JHm25)XK1OrA&Gf_G-Q7+vNGFB4b$1rY+W8pUB56E{QhiNL`4}6%~2=T}oqmPTp@zrNVU}1S*>->`TnOV$BLO@=}4&T&KsHLLlq4_wO ztYvs9Z~MwD>d8qpBqH65e@nu|a_{tLoLCl{H+Bw8EDsXm@!nGa+wH1^9%CEcOWv4x z*ag{42uS^^Fx(AUPfx{iyRi=HxD#@l5RpsQCe=Y39&I-F1J>a^(h)a!GNy?dA;e?< zC%!dc$J0NNh5o&yUReyG_7eiK(Wuu8LaH05xS<`d;oO@k5K46=MC4?z1KRfjo9B!N z`<|QeJ+$vN5a2QXP!l|4G$nL<4h?~}0M6($u#PKDIRw+glJBbhx z?c7p)`5ayM&!kNrGA8Xyovbr2~w|TZ;ihTsn^S!*M~vs zl_3P=l>QY8Qm=a*k6`Lm{=#Snq+XX3B4RsxrKlRsrd}f@HAW%z$^_py1gTdcLOfK3 zl7GUKif~&6PN{r2l5Pc4Dl-WI@marK9HvxkdevY`h56^Zb`314%qB!6PnsW>RPHvO z9)cy6(XIkqQW+w^qk2`vVj667aJ%!&!i(T2Nj8z0(m0}1gMD7sk;J)uVP5B`N-Q7)i(N(jiu9v>PBhOwBxJQRg4 zCII3*AtGu{OR-qgWXtbeP%LVvVur<{TL|%R{;&;;$5opH>Y=Ed3n*k*I%ILd(lk2AE^NhwG& z`SL!S1IedJgir($0sv5OW9-d7eT4gVU>R@UMq(di|)e9xIyF2=TZ$ z-l_%?5PMqwVq$^|D9Q){p^p-R@x;^=i$=Hr!iEr${G4Pg8aXdDwjLrAZ!GR&Vxou; zkGp$4FifU2qXCuF3<4;+2mzr8z=-d^jLK*^0TALGB0@7t59@UyeCF=43#C+EQ71|% zqmNr$vkZHfJUx+HFd#7LZ{#W%9LOMmB5=W!J1{ulNdQJyb+O0F9d(R00T99?FAK&# z>R`Go1^Hoc;Id{F3=S|R&LwMLZ(y-x_}762bOkeZX8j`G>F+6*lx_JuTl=Yhe>sFA zXXmY;qJ7bfHX#@XJ~-a_wz!GP5#j)dD_e74G_>Y0k1wt?o3+QEDtBx4V~2gIWszOZ zS-R=e%q!zB@xA*-eKq?%$^A+-)e>=M!zSKeR22afBO3RNkJ6(3lm{UgiHFp0x5xaX zQV4*s%!*o7>eNT=DX4mRaa9RbYMiOlKW3OZV_(`9bghaqpZ2&k%6WuxYML>!^Fk99 zaOY{C_Jt1W903$dtOIy%{28Vy2*3!n8Ty_!Wr)fn0Ahva-F$lMxDm>$MYiV9w|Xje z%ajzU$RE^e36E@hz9vdz!wQ2PQ+g?b^tzT_y&85bZp)U!$QEXp0E!4YP{+8dg9;%4 zL%L;D_v!mM%7y@l?CxOa%+zk`!NRRhJ_{Qt37?g&YIH7T?9;D5wU#Y1G&@5pj&&(# zREDOe+#XD$wk4~GzdqZ~s1ia^b!G4G!z)`DzONjDQStYV+?1oeteocnh?u}Kz4H}y z%Rk&c!wN^%C0tZm= zcGInX$IRJA6GAY;#g;U{VDLr)ARhW^-=8ed$NaI}trsvqjR{yRX#sn|qsj9#jR#7Y zWAQ$?da##cqm zO83fU*2c%#K7HH96sY{5pn8penf*twQa!yLjBdA&n; zaR^0Y>Gf7%z2^zR@XdVV059De0wB&M-9+|VF}?v4lFL5Nl7*MfCqAGNUOKheJ#%4j z_^Qm5eK11tZ|>GDFk0cu0TeX##KW*07K;}Xf)V_7P9}^{#1jB_^?*xI zbQ+8Rf-a~0!f&g=x{Z!?+bgI>P0sy5Lp7?>m1WqBy3`?l8dNAW&HNYz)@&yA+EVax zA0~hzF@k43R4{BK0Ha&jqzSCqSp-0gUleM-F@i4~<`J3}3}WL(4v-EY%T5{F{On0?B8{ZhOf{V0uVa1 z!^_|+bxq~ce^Sov%=z)5_)(fifWn&4JljCgGUk^JpF4YXa0>kK@jbqno6r*miD zOFX|TPSHjuw5s+v)OPPa#-sDOw|6;S(dJhU1%MXZD|-g7Xl}R5Q45Yy=J0}_j%iq8 zQE8+j3Z)lz^e<&>xBWx_Lf6FsY`?Ymq=GFLcLjv7?bee3g~+5;*m{xkQ~VdSUhp}u zjfU2XzejJNf#GAmGs^4m*LP5kH;p@10)BuqX&cc2;VqblU%`)dhoictWwfT^SMNTf z)#z|gyQYM4JX7L2%5j+kw`PGHuP4Aj+164Orq}p$57od_o10c)T61X-bC~dhnX~Wf zxtL{9W?h@?Gu^r>7$0q4c1Iva}qbuqHH-}@W3wG3;$4lv1Z7(ArB9g!R z+kuG0O>NZzB0>>fFy`Hbe-UiG|D42kh4MW#n_Ox-X9&}%O9?*m!nWP`=WiG_$N+zS`}Bxr@cEQJ3eX0Be&OR0gi=B_ z73%>?H32YS^McdIJjsH*puE3$ODr2O46(4d1T9(52|v)vEGU2$EvX%v1Nm%TFh0aL zLw?r>DwgnszsI~VUzj!`q)tP?u=TYY1`GqA*1Ld}im5Qa2%K+?{t8p*>;3E^JqL_Q zmpl^o!gQMbYE5b2f?ty=yr8c4M$d#XP}loSL~#jNt*S4Ceg-ZWwN_UdxM1(_MC5|q zf*fA3bm=W5p-obq^j|=yMu#243s=8nKG<>q!lqFf^ma{|nyCRKG~R9VPVjPVAwc0w zpr3yMOHTbymt_Dsop9n41LSl{_!oTH-V%8|%UuA;C|7>_j7>svnlNW2zJxyW=^8|( zb0lo=WgD}28NO_(5#GV@vds(V!k3UM&xQ46n_-r-iOB2v$<8>-$(7#*_6sPO@yxR{ z7l9ei;dgvE%b6xzW(%{NUcO1kV3zafV$Lk*F?w|@?s#(bx#i~Z_0R#OMCA2`ZcXff z;>z!(myTojY_4~5I&?-YYRkqBD6Qzj*a4Nft_(Y%-i&Bt2h@(^9@rVhmFHqHmgszX zNYpibYs`9lIJokANn98{6hFkH(JE!TRubO@{=A9ke9C{Pg3hN7(Z(s@e7bw=5IUu} z@?5f|7aty3l-KE0!gdS%-{Z>f-Es%;|8J;sFFaHwV~7h9$^{Sn0RBs9zLUx@E}8OHJE zjUU7~UajH*#maP6|MxBS#5^8XpT{(%Vn6>ABCm(7yK$QJH<91(^t|wAKGDI%3+nmT z99n=sV~r4bBdF((S~b}a>iGj)PgX-c|1_tYSkFJ*k0Z}lgzZCzq@7Jov=*IR&73P% z!JO`T^$BQ^G$>fM4RX90lX%fm>8D@G2f5vpPvbU%LvnBEzo+1kTsG`y4i3p{_Y=@k zDR#|M1#mtS^}Q78`jR%@#=5>e>xE7Zv%@2uT{DxQuCKgWc^HOCCb}P=PQPyLVAk0A z9)rHV?qdx0^;tYE&4a$a+N2?yo#}nd{O0qW(C=4V?T`I_jpP5$f)(q(d(&{m`tQeX zCHTfmWHyJuh_$uNSp1~>5cJXsR<_7qNE#4fhf~%I> z_4qg76CTU&mjG9-MMejN;i%;&ego@yzg1d9z)?#y;er+%wfs6{1t5MuFPzp{YQ@Iy zQ+T$RL2c$2qvIM7zklK~0k`11EQORIeqW>2ira6+y{|Swai~IDANGb0geR|u-q1-V z?SCPDZ+Y4Rr`b*vn4=ebU|ei5G#+TREyS_9?YWRs+;Y`pOHA6{2AxYd^mnz>Xgc46po8YN2P^4mEJW@w$OriMTsv+DTR zMps|WPYiXEsNw#agaYmqyHPnpxzpHL1rcu+nUs!!BE2G4wpF2OTzl1t^ zStxz;oo|$d#m^g}Rn`Bg<@R>OfeJfjlb(D z3z@JTGbapFi{T9@5GT}+cX%^yBOnYxUm!v7#wEn7t> zxWoTpI8n#8O(mI{eoYit4ND5EzW8*of$Gs0-S&<4|3Rq_T}tX>8w;XGU#&m&^d~hV zLmU4OR%}#wA9T5wih7?q9sWgFxr}Q0@q%(Y_!lcD>iG8UWi1!#N-3@yj@H@vS!U`7 zYGjV*zS_?@)SumHXL2R0DCHXoYbGR@QO^S;@Ly>8{Bt6(PiW}mWz+|%HkgYrfk4_8gD?+`F5K+rX>R-n~qtXtdj#qeR zU{J}7sNqA;Rj^TM?l5hQ!6P%LU<@8f{knveO2u7#qfn_d)wu$lP_`+3&%p^*NYru% zBjgB9s4k+8Grv#9-fw53hGz}Ta|S1r;AE*>a6)+)gkg>MaDsj_IHAlt-pYUzilSY% z&4#B`c9kAJrMg5e@3x7vfTuK>sAHw{&1c{#wIOP_??9RzJf$IO>C50LjgOAK4=*XT z^ut1UNuONZdjwum0lPs_*b|8Oz4$Tg2?!Fkd`qGl)DEOMl=50|dTA4Ld|0 zn*@Ub9=|1nU{Ii>&}KSpKu5{XIspR&Iu@^zV1Qtp)~oYSIJh^X1e?(26SaJIP5KWg z9IPkmc;_#-cqk~GLe#L-=-FYq zI=mp_@c>cFdo{%t?_3tX&yk1Ft{^Krd0o@kz*JcTD3 zt!unXy`;Mtn?-N&MDuiH2cC3J5cTYJZ5O6!6^M46MAY&yXiJ)PI&UZPc=N#$v__91 zYWT$Dg&3nr78oC4+>t~b^FHrZfthaAn_*h872_*ciL>0(ljq>}i_Q;M+b+ZY z9sT})J}BZ>uR9T+!C{wX{A91^szm+jESO|mzLxC;(B!D&%ky9UyYZ)$l9bk+bV#Oz zu{wO=mS0pWHfY_N_A_L%mvO<(y9 zI;5kCI!Xh3E$vBJm`U7*5^1?;Wg{q&zP!V`3`(TSiY0eK z>2xxY&qL|^bRl@OnTXQ}qLxn(@#;v_v7N7L0R)x45HdB31)GTVfLmoi+}o+S5g; zN^l9~iq}sO6;I$&Dmsusqx9G=uPqN2pMq4(T;5w8$|d56DHuB=8y=(g5%s#kr4^x+rS(dD2Ufkjfe=cHh}kNKzYicM z^u%8i-wlCFAMoAaC1Q2!ao#+T?~U|hA?$qa`<8;8&pJfCPE7B{b9<7-derYz7g*tW zeJN41=e_z3pz~RD#~;-Dd*Z!sKvK01sCiHF7;{j1x@3d0(O}x#IPH1gkx1R(#GBvKR+@ym4*lZ`*_htz8V5v!KpEh3;jk6yA$66*H2;_@C5ufK@+><*5-2on!n z?U+o&?9D@;r_)fkC)@N@3N{}2&zfP~-WH-(XXrVgt=8=OoGCEHex8U+im2BDB0f!p z3q78?buz+4JBAT4Yck`d2Y*-M3caDfp;ZYA65EjaE9P#+F{ubvfSL0IyLPWM?&m0gU=fpp603mWu>dgq; z4FhI=`3%Zv$>GUqprjrXZTUd>-e$Nv-Ylj$;?s0`A4;PsL_EeSN$i8eqIuAE5OFD5%-C$nk=FiLW5Xr%e>8136xK zcXBjb&t=aWWC9-;-hTvn0!_4Kc=Xubfd#Do=ON-#XS)Ofg5?$*@i>RKKN8OCr}>w8 z0#FD~TyYzK!jp&VgaIhX6e*8`4v3>!sUZ+h5Pk26i3Yw^9C5iKIl~w_BpitN&YSEDd=LUyZQ4YxLCduamg>T*84?vHswIX=jP-JzMHIz#gd4}1=^Y`A=mT8 z@F|*DHkr%r2NO%$`XxK-pHwjevc2ZO{kp^2@FlhWEb0m`sRz-P-}s9AXaIo(%p(uF8UBt~nOV}j9P@}@^nGBr%JpXI(gSVA@J^6~-*Bn~Be zVd+#J(SoN}mL0Dj*F=@CTl?Ygt9(i%r)gq~TnlwM?h&Tp;-1w!fi#>qa~!MK{C@>v z!O;l){U65XB-@N|aA47+TbPD>943X0M+$|77@s@0*Z|{m@!6dCoO!zoRmIuP7{2L_#U$e= zUElsYt?)?~HCl-Heaq!?{G=;%c!!^K>HJ!o*iSl|3MX1`?j+$I<(sov@3`)vsY$KW z+t|Cgrz46P0iyjVXA{2#{EsQ0yZg=+rGKNeek#?dp3Gn#5-qr-n=ko@a0=CAs_89# z>MM0d=CjMMgE7p;qWKu%v_GkehDM|JbJ5V~>@CiIVbQhhfBhDi=?uM*kDp*~^Z5&* zVDR^TL;U2*(v8N9XOX!uIvT633b2sS_Qo%CH0~u@uU*nq^kCCHCUUEkokSu#U}R|3 z5}uLAxDf5vk@(bJ%ZnYSGG_A_GZV(e z@<>jrr92(CZh@iEO&3M}J~;k~$`6>0wVbWbeyYOI=<0DISj+iz8b|9z_IqLx>4cFZ zSX`=-Y@H7CJFkfL`|Q@v2lG5?Lee2nL@Ja_tJc9DPaBFsPs(BBUmG-+K3y1@56z{E zd~NnabLm8pP_$qkAj;^Qx30OoKsVz;lv9Uxw#Jv8Y{``U8nkRa8ji(Ks;~Mbv|!Gh zDufoyikeQ*O$*zYD<8I@Wix{&(yHgySDa26B+{tX%@H3Cu5>!{v<2UZS<^bvbgF!o z6RYhVYl6!ulQY_IIc26(-U0BC+Q){Y>GV5MRy+KLu`ghND5I9$OVRK^FCo&&+BOm$ z4h8S*km2Y?%|=Hr+wY2s=p6e;1jNT3>VUT$f zQBJN#d-1$4CS-LU&ifX3kHWd0W{PI_AAx)Qb+12ouUC#w#e03?O=Z0AuO!lH&k_av zy`4>z(Qa)9e{H$SDWiBJ{+_En^Tr<06x&SvwRJ8D{{uau`cj4XYik!+jK8)oN>W+l zGX0(5N-M$o6jvA)_b1XQ__`Q|$+^IfcXGO{cTa zNu3{Z2%Xe9HtJ}-engbjfr+Q;pp0&p`bS$RqjR#_^yLZ7{`oX3)W$Zt$XYH9W0Dm1P*!E)?-6fy5SVp&9uNcedwvJxUgYr5{Itt6{&Weqn4Xk&SS#ub$ zUiA$LKfxK5p&erdtoOqT?R;=T9n^O*1=Kq>F;{~I=abymMf|{erIv*#t(ka0PvS0&GwGKl4PWRxK9lEhil<%AK=b&z9np4;n z%iE>Y2BQZ@pk3!sug(R?f<;=*S_E0JN3Ow`1uJ64($}`LVKDxiODfqg7&XC{ud$GA zt(FqV`WX>XN_iTs=7+I4mF+>;TD{{^M<s{$2wgjq`8IA6ChYl!# zgX&ls)tLJt4N9Zx_bO*?0`(?hl)cKcs%*6)|`S6Dp%>O zk9~_F)vH9=@CEd?f+v)#WGV`GT?CKkn|~5+;PD(cad1C)JS`JH=zu45rB&fu2&L}f zR~`V9Dvw~zLN-N9|EF{Wo(S)RnC*0;Y;=}BM_21cqGa4HLaM=}`c)$39hg+hausvH zq*`67FbJ;JN()_#)%u5ypv`ubX7(${0dtj3=^OV5$O%UgVY5GH=0x`^bT!3QGQmIP zW5H(YsH%ZB+e4lF9bmKly#5*5a*qb>!d$SlvMTz7XOGbnfFyH2Q98v-N)gZO6Bcmzg+5`gI8&D_nl%c`hMM07WAQte;XROS&i*han15~` zl{#gq5o|h05T)bW@p}<$K5Qb&=H_WvZD_}V^>;p?D?}?JB zzfy-wC)=hE;?jv{VWSFc$4)4%35KPUJI}7-(n)K@ek>@^j|fYFf`UX)I;_ySF!!T3 z6cqfJ$PqT*HpS3<&`>a*C>hzoc5Em}efF&e8Va`G>u!gFf;S3UZ=s-|w0i;;6x>-F zT?r0{KT(q%V31H{9Y^V`9qqwE!dI-a0b$dswI2rzA0FojnZb`!0$`AEaPhSwFgSGH zrU%jC&{?yy7aR`1K3_$LL#orRdU&f7B&T9==u4t>23L6CQ{73FO;WvQNon*KMn7;H z3YmQc_wcFSf3NPfp))(z6Z{jO>gpu(e0Zwozx2eX`lkO~EIwUElkfg^qI5!uF!=tA zqij~ZYsO(RE23nY=*if8`tiUHY(7oo{fo_~!m4Rj|iKV1M_5Hq>5g)VLjIEO1((KRVtlLZwjG4_x$&qr%<13UF$I_JkU&)oY=4s z>UMt<D?|!b*F($g;2N5>gY@G0eWgQVR9tvwjVd`z`v_%klp&9Y}n=$p$ zv(*8=Sh&K$;=FlLM0f|~PLzwejd0%cg9Fs<3mj!rQr0prNWY)bG(V_%;^J$jCcrV` zn&JTEuQ96jRQUlDQFcLM-uQmXe^mbC*nk4+j_8#&Z&&qG7DV|h%-X;@t>`v}``y2R zD4jDX3~DK9qHJcIv&jNh%T+QN_L69|GTT&x=yj9pO+>G0)+&f=LD)ouEG5u$E zm*F>Rv-;PGPAfhzN2O}ao`rv-q=@pl%l`|@YPjG1N<`^!b-@-QY(&=wV_8i;5i%bc z4J@mPDcBoB!{Q4Rrl8zpr+FLIOq9zDU(4=!J>irFQ8r@o z0y`}q`%x#MRgv*Ro={05k&|@7E4fYi)CQTp3h&+Q z-|_UkXXfS%m7A&Tm&#wrJWbg#5c|)Edi){m&c1$jq&DZ{b^ACg>Jm{tGtV06!|vxd zA}sC`rK3%R#||QFQh7F}z)t7}B4n-#EW+7OC3^K9-2Ify=pLV*pUu4X+GPYop;E~l zVbV^7#%{N^5yOf$M#DJemjv66!~JG#BEn-I`KYuDnOzTUH{5SRUm`>r#(TeocAS61 z?d`n*4UCELW^BiytBKIKU>Atp?;~rXCPKf@?8pQp6eo%BSgX?Q3Kh*avJI7>`+Zo` z4gtkRB1E2=F2}C;>ZFw@M9#XdT^QVEcnr67d!?okb zedqyO^*j@;)g9w+Vei*2B1A6#JrD-g>R!QejM@I{%{~Az+xJ|dQIq%_%IS=oKYxWw z>4k%H8o_4kL4?Pn(49Tdl5HGXjh9bBi^%kMj`U-$5@AFFIPG+zmohHHsdUWSS!du> zs!xQ*dK3PAFf>}T_4+{=8tpYdgK|5U2#-RcGeIyks>r*<34e>uoJIMaPK3z4m51@y zE_XqEEd183se3T4iDrN89uc82rzSZFTB~Nf+k`Dw>9)T*porXp2#>pK?6Iia!+whw zlvZsWJcz~Qc0`Cwwpd#UEm!$-e7d3KYTL|#^8JD>lpzQWR%a-ec_F6TpP!-wm=1IS z4Z@&gHAi@?A;RHT_k)=*9>@Le*C#?`hIt2KI!7n<`+({8#)lNZ*qk8|8mUIQBM`v| zd0jmoA{bpn7t|8r!4Tb$EPUubL@@YnE}`+ee>Er(R*0lmJxYNH#<)K%k0FAgeQtGm zk;5k{mI@I!-@!pJG;wq@#w$rej-Hd z#;AF~LekThnYfTNrdabYEF`UR<>-uoRrU$cgz|rEy$Li|Ul;#x3`JCgk`hspq70$7 zsHjZMq{Nrdq@+mE)kGx;l?W-)fJ(*^H}gCv^E^bRk9p>Q&v8BL_xrEkv(~eom52Jc z_nf_7`<%VsZ@YTU(@G!vNiBjd4A|(xLy0aN-q)PKN+?%o?k}bb5u4I)-z*N7lB*xM zV&xQ(lV7lMN?3oQ3YKzt$`X&HTvYrHW9^p~x-MW@cM{3%lIQ8V;o=Rq2qfh~IMLic zbAK)4 zOLZgC3tmds4WDg_osk%gG|m09F1ANHI)0ARSbnD~bgnMg(W62l#p7`fTsVMg0~40h$)LG^nmYg=NI>U48iBe7wsE8%bY9m6YepaB`z=stUqU9a-^2W@4N|0!fz}oN^SNH28B{Z800eCLB z+3N{0ga(7}(S?CY^6qy}`76PME*ye5m#Cn*;6F6?^QbFGZat9-`L5Dz_rG8l z#}-M4ewbj3BzSq;4_!{bf^2*_ZG0~PpZl3j9K+&so&S`kQ92%>>w*Ni>sYKVfvy{j zoOjfqbi9`4e!X)=_+0S$5d)l#j}Yy>SgmeCbra6Vb~LBkS_?}dvsmj258dJ;{qX(m zBDovt@nkp6?YGQ@IC@Mfz|ubaB=U+Ke7bSSu_6_=(-{A5*QG-el(%ekK@|3uT`c20 z%#%oYz^|yo&FjW{n7mLA2*KKV7<6e;cjUr(dH+!Mk@!p78 zU;+6lGzcHVZLyNW3J8zlC-AXdw^kzVXox8=u=4DyAzMw=amR!HXAZ{*i$1dE$X49o zpe`VekL`+E{!qtuR9Wo(>LEV2yUwx>pW7AEoDP>WR6e>Ekxe`PI~@D`0c5oM`W$1g zCo#8E*gj#E-h4~3hSb+NDZQ$t0P4=2d1B4nK>npM{&cskL|(b8ij<@|er7!-QdL+%cHF2Q zS$X+6IQn((I#pRp@{VgNGQKr}kNncG?LL_#QytIevdRj`Jm_w=q$V3o+igaD2mgV- z<))$^@6$>D&%vn!y?=ovuY5W38*b-s9$%H!23_vQ366>KOp@b*-MzPY&9E|9lW>gt zKuXY@KCxThO+)1~d3DzLWuQzG?0uNSC_ z5X>kbTWE|=2x?dpHIhfJ*kK{xbh`{H8hJ#U7iE$Hwfk}Ty-|ahqgsh+z5sO5I8VJ% zKQ?lpi$y`jeq4SZ8EL~8i3i7@;_^Gfa2l82K63K7{2nY^ip%eLD*kD_8TWiT(olO* zi;WeD+tSoaitp3M6Or6RPz&z)bQ3Jbj>I=q4Y9f{N61{y=R@If7o=`mvbGDW;m%Vr z2gfb!{Srb`ZI}0#+i1AVg{Iz(%a1HX!)W(%^YJj+_hS~=QCaDMr7oH+JGYGaHxfJ| zI5U^x;j&{N?OTvp%tDOCp>TJ}a(u~@OH=I@CBZUu$uv*Ba)G9I(4~}Q*%D54DJ67W z0DD4b?+S=SmrU0u&G98uX{?bBx?^%bvmG0cy`-r)LP(CX-)SYtd8!S1P>NUmADVhk zUn#>b*9F`aJCR?{r7?fJ>aA3C@v1NUm%a+E`l!YAVrbQ0Tw-K^cF8`Pic2&-uoL>% zKvnF7UNlp?0gdtAj9uA-8k4t{JdrlzCSH>Zx|DHe@{Wt@AJCj`;%gc_557Dh2=##NHx&X;+SaG7vV)&?Y8w*K{xc%&7d75*y#3773Z z{;UpZ!w>KOY=E58UvkRGA}x5$DI@HJez25jT!NsK%ig+rVCAw9#(5Q_TsH7030tT4 zefEBZEEc!u`gFc?X#_^_yYUXew~(@M7+WmPyyIPNX&6k3vhdwP7K=U+zD&E1ABTel zmszixmBSMEqvDjsBJl6dA4Lv}GVx6hk;CGaQcvu#c>b~ZBVup5J1KIL@ndIvHf&zd zJL7q`o~+%NcJu72+Yo*$KqWx5m6Vc{y!0_5kJKNL-oI71nOx^`ob!=OJh>tZf~haCKgP$=OFa4ym%fmrPy+b$W}tG6l=oI>_e^;i{6}I z8m3$<(z6${AuHBtCv6vGt6}fD$q$*w&O_>>51iOsR*r_!h<5dt$cAGcLd`de{Y9=C z6(ZiV`BWw4^S|-5NiJEbK1LCz+HSL?A8 zN=NNSDdgvS)N!GV^to>6>`zMEdpG@mkjh>5@PNsmHZqHb(v(E7e>0QQVH#0gK8k~ez`1^%e?g1v%<{937c@>R%NdF1i7gqrSCO8tDUi)>xOkA1&$q>8ccw_M87(j^IX zaQ3qzW&2HZkScp0H6Xp-dLa`)dc9RNrAlUe!+O2PXeeFzTe_xtyqBznIS5sIc>v4x z3b;99x!&Hmek|9!)9Qr{lIvZ^9V>_Ade2P6m8nQKlNY^eKclwYpERWAeim&TB9!__ zy_<7Xx+ZB2p7)ERA=GMy70>&vzOxBCBHZiTh8+rZ^-*nsK z7wO*F!hPSV8?^J5^L`461EE!mxA%|t!CJ441g6%OX9w|E?L4!QG^Li%Og9gut0uZ| zSL-~4u3YJS0X3=KHBrG$s$y*teyB-xLBw2B>pb(RRGEIw@a%^|EexIH@lK+yJ+KKr z%@A``YPOhg=%wy@){BKV3HH~K#>J%4y8r6ISaEk{&;EK+*uu13D7zEx-!pMYi26;| z{y8O^8QKlMXlTuK6+zKW%vVEf=HJe7dBpOQX5(BX?`5qcV6^JTHd}i})weLT4 zW}|tm7N6OOUypY~XFT)FHyjgR@oT6F#%Sm~U1=Sv+U5&|G?cO>Fl)wjK881EOcc1h zn#upXyA)?1O3R+{;YR<_xVmU_b$3OW&U9;uA8xnPw;>3`Bx+O}m#fT^w$a&0;m8HCi5+ut8L z7)h4GPcyc6OEhc9x7;#6+@B}F#o%yxrI1FFgV`_pVp9aT($E?wdZjfvFblY7CY*;< zQ<@o#Xed4AA^ygpF@w|&*qUaDT-da#y{?Q@yAXNVB(nzk zXs8sR8SzqOJZ?r~e(sflOeyDq!k1>o7#cV}AJl(|n$hNYL7isE_1^r($dvM)?N07| zg6c-Q+IR}PQkv3``2J4{meI(U`0*X-`SGXkd5>f?L}+Lns`A4!8fk1QSVm*pT1Bj) zVM{Zl?NV2)qLC2Nse)8Aw*EX?g5>-dG$V$}Ovj)Ji+#1LSqNdui9|BqP!`Sei__5f zdHmuEH1BVJ?ztHhlqxAu`GREI!{qq!WzK z_Z?Q$51{k?k2E7{|0lqM7GZ(N)-`C+=J>Ofp+e&<&5YiB`z%l)azkhxUWES|q(o8S z;XpIw=1d;EXjgnAHse*B>YAX2R&6#7iDZjP7}9$A(a+QqOliH$Go2=e#@D1c&3r3Q zH;f{!mw9B&1>+EgEUPj4JKJww(q+bBsv(rL!PEp$VdT-$P_5lu9$* z=&?Cyl&6{RRN8KAC^XM(YiNjc`V@!_g_=wlN06bA>FRUXQ0ObohzI1_WrSjDf&Ihv z?ZQnB|3kXvVROtlk9kCL@n%OMg%|Ya}rS-QBtN*~iREI~3jC4|Qiq-R2 zZ7xXCjQBTTUmh}_KfrpT6`9os@Xs;b2bvk(Xy!AC8%wvN6cIdWWYeMq0j6dnPG00IG+3qcPn|s%SK`MW?WNF)(0EJ7|>3);zBa<5zOI&L#0evZ{=h`MM zq=eGDpRmD^NHd#X>Evj>cpLem zbknX!(>0JfA}m`hpGm%x?Wy~+vk?yexFEBjrWhvm1l-dDD?xJM@7sI#3{!GFb4a)A zn#^#EQ6tD8$daWCZi$Pgsb%;zZH-?^+Fci~Z#L zhKdfDXFC4)^ppLDJy1Y1U&_-zyL(sl!g(6mhC_2cH+Z(eOv6Znf%9+jjZEZ2{k(Q? z(>Oa4ozqI%-1h3#sEHx3%Q+L+~ew%8co@PFl%In$4<#LYM zI>_e^=8?|S>D7VVxz~o-8_8GlHOk24vPf*99G7z!DZV~dC@-&x>{n=%KmvzG6QZ(6 z;82!Ey1Vi}w)e%HJU#)cj8&CY#Omj`P#QDx_u?1BVL*CdRM?rGAmnox|Zyrk&E+m`EBBB8Y!=Q zB|v{iJDgqnBJ%;|C8m3(kQ~Sj1LQ;mcl59&0qi)-1$Z*K9o1 z`r8ufWs#WrQ8PYHT z50PJRNj|v`bv!Vumg81!_M|@C@t|fCj$66K3w`63G{_3Fv*LkhAAd#Rfbd1?HCz2oMPYvz=98v2ba zFGw_BuS)@|dpGef;OjMqU|+%yzvSOUA=yjD{>^{mp-nD5^t?wS*%o;&Ig+5T`MHt0 z2Eyiw)7#D>%(fB`V?$@V+9t0rBFvVQ%~&J(G10jgbt0$JL{j>UH6Jx1&l8KS*iqcMoEB{M(!u{1 zdDglXHzG@$gyKfzwO+SzBeKJFUEGLV6~c!bm&a+KS+A^#Pe#LOAPHLjcnqD4hSR_@ zNrZ(cprcggnH}%ZQR*!%zX^0SdTY9n4V{c0doYGiMipz!9-x!ajzpgnbd=iOI7c*r zP42Ep-Tr*aU+gk*Vcp{T16+?Ruq;4<2%LbcD`PLuc<1q<>G+rjtnz3fEk}!&A(We}YG`9Bc z2yTSg=#MKl^#{Oqo(9(!xit_|?jiiSIvi3~dpb+4rJT~z_}}bN{R%CQUyvnMEx^p{ zC!tdO5u}f<*x20NLO`)`%JTz{!(in5e^u_1wJ@;8u+yt2kNje9p=act1$@uVi#wDW zNcUW)S5MZOk{sC!ziZo8kjE4Fe>$WmfxJ!4&hEB!((S#KCUYIuq3!19W z*~U{QR*&x=Y`s}WcCOZ-{vZS*TyRf^wOFt=A8HNTOOQ8B!~UgYKq*9t{{K zBC=dA-F1*i^TdTBx8w~6Dc38SA3BaSdTA0M8p6!@GHkVC>u8!&grRzct4A&wk zz1P(Ly|WF3u6)${xhV}!>g91~zlee}?Pa&O4VA(?Uwn!ZBU)Xl&ZjjrP=s`a^=|u~ zPL9(6QR`uJwxsqqc^i1FrDqyoLALE|L0vHHc7bet4ys?c<5TlVj!?3~-&oRd^dni7 znR_NhyBOZkfZ<)@yYQw&6WK-cgimr{;z8LO@&U~cjR}O>RK~7GlJiiv<*4T`C_f#! z=c3PdvikR3+ogX$l1-dyd|QwFBtxsu${^FIy-SW_!va|vF!~OQV$Y}#G*CoLT*IDG zqBK7gmg!+n$fa2f>$TX_-%&W&plkozbZmVd(sG-ww z5)BmHdnUM$hGQNL5Gn$~Sh%dwCJPIfDZuVvBwY5Aoev9_Nzv)L@#Zm0vaqMF#iv2ft%Wgxt z!kh*U(`V&)a!Q3xw-z*Du+r(clIDqvZl5C1q*cxwKMG+q?C89W_pSoCc0I?fZJ@s&ziy_R@giNT*{K8Yl!iL?qGG ztULyYc8G9hK8&tfXJf4JRcr6gxAV>M2E`X`biUW5c_Jp~(r(0*(iA@s=$uY_;V}$}skN;b5}n`sVMsKO zO~O$5lI8(RI=?^CUA+Sdm2%dMokT*V^T}olo$sH~$tYF8KNks=&gYWDbpC%dmcJhf zmEI0XBEBM}($8DVOpsD(2c6%;?2L<$go5M-Nkb&L@Uw7D2=aPuq4WLLmnmULFHlNp z?^)#a$}|>ffb;=f==|Sk68H;wy?$>S9YK-{;Yn(4NOIvfo!&Fk@{>_J-c~k32KVpH zs=eSqgF*ImzIR#mAO#H$4UL{NL+yBag1z!|u8zC^|t2Xz- z!vCdXTRPv5(dqgXn?%I-kFoztx2Ng+KYVke_4#{^5YiI(xNPPIso=Tac8z!i^wa5G zf&WIC`g!X7ueiF1%Wr)TsfP1$I`Gi>zFBH6U&mO5`=`ltkSqps>6V$!|3=GoI(ib5 z!AVf~&B2Cd^7@v|mk2kC{j_3OkH4y;e8hX(-2W8=&obeeKb|e*IXWG`^XYwL7|=~F zUXRi>kp?1T=kpGGbdm4qbbIq$#mNJw+DU8WqvJ$s9tiBc^)H;QoivTv)@Y?x0srpr zyZ*9>nH=&Wm2OeCbu~^Y zPH|`;T{d!@IzX(ihf2#=;bB=>B+~|_1|MV+?=qdb#UKubjkJzbXOzI;`mMVx`+J~{ zPRG*B?-mSnl>n#TT)Mv7o8?sawGDRA>DK7kNzUrmneeSRk+*2cM(Fa5`qNDFx&#`vI5ed>uLxuJY2e z3|Q!NOWwU(;eS35SU>GJ_bsmoWKJL6^)G;g{uS%LTk|E5YQ_wgT-j98=itlp6N+VU zY~)1LlFV?@pH9c7Mu%UFvb-YeGjj=?ucIotEE5bP$zeL(a;Zfo9~p@uqa=SZ@-vDd z*Iz(nSigYW^VG+Y9b(C#^*J8M$77e$j0f`Z_!;>S+jxx9>3ASQxe^%+Y0~LBd_V6v z@(a?7naj4s0L(l2-*tcsDK8$|rEQwFAkKX4` zL8sS;>1_F^#@8RURA?$l=Agr9v)9A8gMy^9XYhn(2s*r;GQX3I4zEYK9&Sa4*Fq(R zSdyuX7z%YpnoJxA*-jx1r(jh%KmX8b@SM!W8ctGuDn3ZzSVl$#OERt1GsTL==LH#9 z!^xj>VDvjB;UwjG{0x$Cy0X9*OE{VDpUaL?D*pIBV0pJ-CAvQ-o>0V1D`)9!NysUs zJVRYA4|=|^pbG&HsZY3z<#o+m`g~D#4qqUQ8f<)yE)brACcZ%UVfAMmT?x^c@W%uhx;3L+$tc=SVzwyqb_g?fL|y4HfC4 zetkFNY6;Y@AGT1t81?ISqVz~*M@voR7IY<}@*(OAy2Ht_es+dx93q|RY?=J60XG)e zrpIhTS2Dx5IFF$|qEB@8{5`lFU*Q}s)y7vip5A8m=n5xjs;eWvj0lGnPZLI zYm(?>(=naSgWPQdcgNUqb5b7Ajnl=*&8FnTH!MEulO!9B#AmPY-A_j1voen9I!LT` zJ{fsGWBTlSvIy?dx#Xw+>2*keh-t@Mu36lzQffs|3Pn#fx8SK&t(cL7BzX_myDQTGSO&7E-o_Ky5L9og8sn%$NKk0n|ogN++YHGp#bnLizV8 zaW^-rCDUD0YyXGyaWG4!HS>*B!Q!r!XIFiv6pcmJN1mbP|3svZ8E~$P1;waa*g^T7 zhxK>lpe&=zvyz>1P#!lp62T#g5CYysgB+Y1tU~AsB0{_^S~V^H*IRRZakR zmYld#)LA-U2eoa*MWdb|#3v((20L{6*eu9n6PFJT%KEH^3tPygBA3;xF8zX>k$0a??5raBFE()0oymlV<%>To z9;Ze(n6w{hQiaESZV)wuTxGEa(4cccmW}MngX(qvi~VXZ?fY84)9s#Ljv-j(auTTd> z;_d_cs2NMRA$!83J-Pu-(aGW5^T^|jfdUX>pUVZ$oBA}fK9zv@y}8(bbA``u?%X1% zzGRurtrAD>n|Amb)4i8$GNioXze{5?`VBI7RvRjwEd!B^N=tVgs-Xk-5g*W%G8U4W3w}r^P{&>GKc1~k= zT8y>eCFw{RPUnVqbR?anbHT22xmY{?0Ui5SmT^(0)LgSzK*cdy3Xb2}#5ucB_dGbmRORJ)zsR@pm9v*92eDwosA!G{i|?sP6VoUD#D z9?sCQpL?8hGm>oR-Ry!T8}?_JW61`?_7hm+L4XdmX@u!T)WE1hhx+s_Ct@P02F3y^ z7Z9j}QTV&z7@ABd9gm)L zuE;TCu0TDW*>ouWPUi;cvvZ-^l+Fd6O2;0dp3oI@u^(!y9uPgK$5ZQgC+_h~x^i+9 z^>~s|5x6IGAu$)r7gW!MW*!S`K*nN07fxm(BeMBGw0CwcRR5s^)m}+~rN~!oKF}C9 z&4um_uV39qMr5hlGaQAK5t*wjVL9hSM#tvMUc8z}9IpImNI4RdGxOeCjHJ}FP^_jx zbyl~uGm=^ttM5@p+HmlsC>2So4@b|1?kk@6e2|u0;flC@NJ~y5{ct|gl2f2VbDL-+ zo-;Zj@92OAla3M#FO(rk1wlGiuMOj~LXr#OqtEW4K_jtTYb>$AOvmoB8>YT!Fe%e# zCvy}vn6%l`&>IaVU8F-Zo7-o@_Vy~uA<^<)%JR7XMbhqnbD=tkj-dF*nOw8{+`O41`ujv(`?F4g&UHFo@!&|u#yhVDF*U(O++!2^WHOv7i%En=urBR z4%MM_sI@wBSwL*Ol3YVa$_;erc3()m;B_y8Ejp4rwFELK|344?X}|b<5IeOg^p9OM zTvd2|_2Q#4$o9JN`44jixsDE{K23@t5=!-CFCA*no=IIi=2}mlpd)27N7SN&c zl}P=~HGKJC^1dWbXmKNS@I~wy8~h5XLCxQDL+W7n>iyVc^4o_rY%&=w?I(jwDOb;h zQaDw62b)rErbDeAVaJC|CXMJwIZ#@FO(~rVC$TBzQ`_wg1ahV1a(BkAl&?-+aJX#Q^jR9-{MC2Dk}J9OWs6)Bsf5`F*BJYV-2e>~53g^qlZ+}l^6`9Bb1 z!SjC?a&7Va-v<3~O(f&VHXV5a$#_0C)g-X)=ava6?0S*e{C;_eVIP>MHZz@AkPg$E zEVz)4=XcY{Nd9$ypwQ+q*8Q{@lrfj~PK2q&<1Q5!8L;Gl%lad?e#3lZi#70?MiZ4= zG&~q+1T(PS!wqT2ET;qA2i+R1E#s+0MB$OjcAtIEJvF02Wk0_rqXo}zNpu3|BC~PDqqG{u8IC%oM>m~)kGEzal*c7$fBVoy7wHiaO7jY8Jd2r z1g>uJ=Gad;E?nb0jU5-B5v~&kd6Y-P4myxne=*&Mq*vC_alpp%;vpovd5Vq`VZQ{@ zkSyoeNe?X5B4n8phh#X9yR0-q(kna#r>-FBmBO#i%aQcTjDMHJT1u)#l8z%)d!%0< z{um2eldj#pDM(p54W5+J&7lOrq%&%}ONY`SJ?;6ZIcpr;0y$^yD@9;>>u|_WRUsU; zW-HVPDFFGTv&Ww;PJ?Sd*0GzHmxGyZ&`FbqTsXt>WZR#LVvt|hbLy^IA~>=*_fI(& z!5Vs7-@DDX(S1)D%x~kOohm1#DvAi0-?ui~PVDb7D1-cCK4!}zGRRoppKC1j3gEIx z?vNyF6m*nwS$(O_hn9Qg$M_#mA8_L^he<{$Fze6lYisv(Tk!~AjkS2Qn`RO`RKk>-xkO$ zjOk@RTMQTJeJxd`Q}AYGBN({M?PDPg?!9hp^^inw+vvS6_8eA?ketpm^fsFkslV$w zR&cWhUYRnc^bR)x>*7dZ&xcjO8n{&$dB5OAPEe~{o!-~V^e#9=@8iqg46s>DH@$C1 z>`!Cw7vl%bW61mEdFW&W@_rewx{SSI=66Zg#+`OZfao~AO^>lDFg=)Rf!UpN+c+Wa z78a-VKB6~HoS+noyeuuS8tnomp`%EE$VkX&H4-4Y#Q)SC2@u^>@xh8ko%BB6er1m_ zl4y#fw~-XRuRH7Jw%HGQ8?V+Y*G7^~ne?`G(XCHH5>2j0i?BqK)h1=)KGJA%@H&b$ zod|jxZlJeSf((z(%P%BYpPgz!)~WN0G=tt|iu5XO9N`*APN?&XFpb`JyiW$akaKFV z)M_i_oNB(LU(}2%9gfhuYV0tfauTW8R+R*cAobg@1g}%b`z40nW~M3@*c)c|>_aZ3 zX3Ha4JmEum!OU;FQ7hAhNc}c`OxYHx-^!NjZ$|33?ewlv(7SkINm2$RUHExP?QJC7 zp?5(Nz00l=^fs~jl~7$^7X^*MPu(cS(gKo0Uy*Fl?t6d3M*=WA%)3 zAmmrfz7Cr#g`1td1(sXNVEyTrq6_(JV9zsIedGsxs%>OF@&k6N-9EADT_tR!cfs{P zG1x%3nBFCO|NDylfcfZEzTnX#>>s>xr}HH84<3BB8T$vHb2r2O!AIv7B>^qAKb9g% z?~?RH|Y6ryKWuNwHA52YJ)L%&n^ zcW=CF#-dF#;HmDC8f39FL_Yy7`ju4BPv{!`3Q{AgcOy$CX8Muutcbj?B3}rheLBcu z30@~9*&&CecL$=dCDXOVb5AgVdLoyQWpb^eYX!1Q-hQVaTP!QkzkxM=M3ulr|AxMP zrj%uJBe>EZFpqv>CKr9zBa7v{r2fb8)SZyHj&BgMSU#+$znhtfnTeBW?mxFn*rTEp zn0KLeqf`1`Y{iO?-mKpi>$3zIJ}}( z`HIT1acW8|n)}z7>5rmA|M>#X+}DNZuOANkzrXJ${pVL7sK2YgH<^`MV4^*lsppUV4A42U}C!L6?M&br#*`@>yecl@weBpPIE%iPTFwO||dyMFfa-AHoCO1#KqpZm=h&<}w6A8Vn6YJE-Z1AKhw zYhJH?ok@<<+z}yjPo}x|6vMZ^nSH;n{4Wk+mWOYAb$@ZZTIBS0?kxv|a{4mMvaJ#r z`Y`teL;5e4UD}x;H}~rQkB2(<>dV48;>C`#@~|w#|0MMji649~q;uG_u8idH^}5Wq zi8Y#;^Z)lPH^Y5pvN&v3F8la@zcN+&&&J`@gI&cu_s;&0FZz02W!vGqn2n9ocU1=G z8sGgn90yqLe)jdv;aE+-!Rua8&c5|0S%rNM=W+fIs&^JP z#%=hOcWf9&cuv>yeqs>tNzx@JCwUesuAVo3EIA5I3pU8L{cD3e>rx{bdP9&^CV4-t zybOHs_`P_+uCu(Opm8S4K(E4AT}FVhrk zH(q?k)Ug2$_%AOA;2Z`Y-Jpr3l|#@fdb%;+dkj9Ujm{g5oPiGx2J5aaU{>FL=-~UF z+A0_@e9Xf7^)F;k`7F&@Lf!d3e*S^j%*CYs_T9k!OO0coY!UQ4X!iuz-*4L>B{>Tl zgq#NAo5w)1is{R-JCo3H&ps)fS_^i(OS}{JPlGC(YUg^J8F*9~{MqUI7zjGmx%ACW zLBFs6zXZc+SiDdBu!hwyGGT|^t^AleSe#Zx9Z1AN@mpu54^vGr92QEfWg8xcZ{P0T zd&$KBWzH*m9&Va|CT*`%YQzMLf3kiz8#DtR*5~CGJ?H}-d&Z2RUmF-%zgbg4wNMOX zMOQxy>;)T%=+j4a2Eg>rw}s-F9k8~Wd>7%@2T#5zvz}W$28$>BUd5#hg1nM^kJt|e zeBWSU@J@IPSUL&+%4Ur**wVEqAcE>#0#PgT=zw;(lQ_((QPmD7&;9)-&O$x9q{L7| z_zgsJwr%qIW_WIQEBt=X9}wMH{@=mVSEv)ul^wfo)xzxhi|m32>VR3~S2l|-NjCUa z-z!Y10=DZeX-snMz%FyqV&V(~wy8H}Dn8>a{iZ+`rNf8`e5h!^kkK z+AO3!eCHojS!=Tu9G?XHu#VacLdnz47yMMp0fwiAi>sEiQhJD<7$E9z@LPcwsr*_pCd?CZiUl_MS z$N5iPQrhFNmY86kFlE5QzT3y{QTKmS*J38G%QVB|j3j=I#wi%~R-g3UHwt_HI=A+x zGvH2y&c@2AS>T##tKp2BBtUz!)@0g-F>th&c`cYT1Gj}A7+8^$z}T+maQN92XgyD8 ze5^kX`8%dI1(}Tk+o`HTRpTj;3a%INkF0?^*N@)Xp&bHywr#)JAdx^W*y_lxH=Ig_ zIS<&5$50oJ+kPHXq%NKO7pEGy?P!5Z%uk){H3wm*{LUwrt%-4vW1so6fiVFYj*GK` zyGFomkad{@ISxU)xdzu#mP%fwd8cnUwZW!!uWtM(X@yAG^lLzz0ma!z(?V?-pyS+- zvNxy!&YW&E3J~mr#UBQQxf2+0EaP?d!xQab_VILr>+4Sl&7wOAXL zuM?Ug1Q>A8J2(A!1p^9;_sSYl8V%gDxAw1C(GPY>4Z&=C7!dyKmD`IKqj1Q3P2x>` z2IO67YCCBE7c_1kuWF|P$z);0Ztd6s@Q9bU@UL%%`z|`)yS&<=t?`VQS3$5qdN{1=M(mw`-X+t&G9=VlL=U)r|^}LN`92|zR!n7T+)AjJH zq`m#Ym0DQ&vTupy=|QLpYd+Yj*9HyebIMkvHNlc1vB=eR-5@XW=!KUP{eoRTxQbwVed_48r}l(-f@gIWzf&sy@$F zx2?R}GX!CiP79u}4Z((m1?HBFDbVj16sdT~q&}u&-!mY|r0#N+V}Qe0k^H4J{LFDx z1ALX*azAr#IYitiZZ5Z;U{ZfExa#y{&v7WTPv5B$IR;LpyHw@mo8YmU^I_g)^)P+v zoVwGdDEM$1lO{GnCRP5g{2>$$gxYcG7hUMQ`p6_HpsPAiCJAok} z!VJCMj;|gCqb7s5Y;|og>Kd!uO^!9n)BCMhnu2=K$uXDWys{~Hmw9jt=;TM^Qa(54c zMO?7brfO;zyg9Z{>TxfmuMd{sD<1`+XQX6D7X#S8#hjJfMg4I9n5cGfXml5wcu}q5E%FS-E;CZsG} zF8pPHxR#+;5;rCCkj>co)0zPr&p&-|$d3UnrX_z%rCVT)zg?iPDg*kL8m=5(!+^Vc zg;~0&cb~g*xAR}of50d+Yg4({11(&}&PEpo;4&k5nKcC*ytlRLPV_LSd#afFtq+EP zC+LB>FwsMurR?f{P-#5~V;61H*7^=Z=Id~|tE~0#_CR1!>aJl(I9Yn;`nqATOI*{) z5?u$KqT)f4$-{7cVZbXdjX_AZ*AtweN-LvndAidthrq~2$Un}R0f*k$cx%rKQWF?k<_dP2ezO_q!{zO8zc>uM!M6HQuFc8iG!HgA`&}1(f-8PD-XQspmO8zV63A z4qu#E_><4fg5&!W`G%39@N{X*VnOXvc>3z&XM^3bP>xsVBy$L-&mT#laef`KWrZY0$0N#4?He~yC*ESX9qO{80NZ|*#88DXU5Um)Vn+N ze93~Stqiy*!`v%v*#e`tC|!~Bo!}}~a52j-98{MTt@T-2401n1GyQb3;mEbUpI(bn z?Q)fT0{ZX!VC%V?Ir;YF4O*gSv;~wc!;#kaT3v zewSsfa7u#n>E7fbI4$t-?r+r**jE&D{VNj#1lVtgb^U6AMeJ2mNAxCvw{Xm`TArfh zz`sY$)fq7Kq=_r1ZxU20w{5H0Hw?u$f6ZLa7=oH@2Yt+$#vo+(!uMaOU6nQ&ExPE0lYaY^lfW<;DD>2E9u_>b}Y3~ zqSQFd8)2&h6KvIRt3l;vwd*G+{d9e+WpDtgxARbC;GGg!_r+-KxyW3oQ(V!T7hDRF znnHVnHG1Id*Gc31$pe&*ca?5f)DQvQrVD3Ge1_o0ca3i!!$x7z))RVmT%&N*lD$_? zwi5yd7HPcfYlkuWR2x@!O0i7ezmk1X9?1`#D8$5DUJ*PpJYKvxG*sak#fbGE4TJT zR>Mb4=?p6ANH{ZBtsjS$ZSiXVN%X^#?hf_g*fEe`DlhGT=X__uDO> zQ2|PBzg}!=7w~-iFVCB;56-_#kpJt|0kN&G^b#AIz~Ab>=;-r(;54#CJ@HTzycMYq z^v1v zVmFm`ZTttJM}N9V(;dY+G7heI0@A)@LJ8OyB1GQQG84?6b_t)lXQjb{Kq3Vfd4=AQgDQ~Rq zfhX4-V-Fo;fR$K*)WFhaXqom?4P>RZ)mLZP|5$tA>Phpz<=TU=y@2DhcI5yBId3Un z{bLYxw7*!W@r=NfBJoGgluCyIeY=^*h6mw#cSqxQyGgju)p0id_!!i@sa^MDeGT}r ze~BNuItY(FL`Xrk0pMz0RL2oI1@f2M{wr3P1@A}gf6UiTz$)i(rQE+MWbOk0L*fso9$sFSS!^i ze=SZRZP>03-Dw$tSYo>cdtp0pWd6&pz0nD}Jhu&5@BIVq+ho%o{$`jNP)OYK&0yLWIdbegn*(pc3;R6}G{9t5G;__5UQlDX>zO{;3n8uwft&^ZKs1ZGgtcP{Lh3GJ~2Esy&RxqFk)b5CXW57Q0;9|`URQ<0~ZN{RJ(E;mC z3ioAZ_JaG8bq+Pnlt$BqmQ}@;U0}|$FjVa;1J-oL3R_C{f>EPD!?BP)NGNBTmR?^6 zc|JY*d2VgMb#VRL%k~wZsrf=Eg}OuvYdjw@xaT8?I_-L6ESwL{p2_8)X4Sg_a}7(u z^>xtnOimj}#QZC-F)M@c(_s^wOS(WrKF5pjZ0>;z9~JozsCI$H(-%g()ac_wp<1pe z%>fX!V}-_3J)mOoc;=Vo5FFiRdX1xW6f9Q!)Kp(KKtU(-=TNsP;D7lk{onEl7*ad9 zxl3ylwrg9;8#<4E25(iH_BbJbr+RAYc3HL9@IO` z>ha{}SkwwdF}n}gQb#Quv(}&3jk@5_;i?KnqYT)5O2M)7NfXpsj9jv$C^@kpYHz%A z8K~)SKh+xU0EgpK_2F90pwKd6byd9$emn2u4ZqP1ff5Tevxfdc*QXm1@yk2FKl$a( zkHXzhn`NMUl;Q!Uk3`8AorerqF&wKGA=90NlUG28HqtK#Ev!`BZf;MCDFJqP23c*a5GVbs6?(tq@>2Rj4(_ zT@7lw<*j;l1EA$$8aqLCm)WKX;YHN_L;F2iL;)_d7tkVF###P$CjTW83i>onw2VPr#+g$q!CrT(A?fy+ckA9JBvmIm?Z72n|jX$V0 zN3_D#2cP$&MfdDS0bX=sAC!A86aQeg!Qdx%*l&o{KUtA=y#pQ?IQ&M7@x$H~cro6( z;&T}-#;8TNnOsJD;ac7AOScoLpJQo|7E++V!z*?-TBKg<{qQ2)X9YP=t<$R>80ddxjaF|{ksw~ZPW3`~^*(EAEZzAZ3FLUmb4d*pXjYdd z7j}ZxgNd^bC2fIi{Z+#H5LK36>Ut()-Uj-o%qC=2(_o5h^g3kF0U7}>B}|XELdEEz zCoWW3uewCn^aNG5Z%a-tAQZydL1Wq@Ev~T(#K#(*-S%d{pR=H|O0)~ky{|2DOc;RC zw|P2?bNWHuy7$f7x<1&S!O%&0F$Cui#kPvXP_EWpsT~z={ZR3-V_?a#K`3#*X=i(v zDl#?1dtVmy!^y*)LaF==IP*+BMK7rb48JaXkXXn7gS<1u(77j_P&~Xa%0RsfDs86k z-QCs;`rB`-AEgF-dKzEc=nWWzUfoaL3Q-K`%BfgB*w6>F?%AiTlzZX&2ks?HmFhre zhivbWP%88W?RvA}audWBmp`edy4`!8T;jg`q!^aMx%7sxPPkUQ{Pr*HF7RKXqPS>V z9|RsD@2 z{JuNLtkDXRh9a1Jj!Ui5LEvLaL|bF>aonVg31{sas6>SG>2u0C8dYq4eqK212AaIBeIpu)3 zLEVNY2XOv4gB_8A?T8O(fcr! z|NZ@ss8SeGu{(hpOKVr47WG9c{rm96X^9pIJxJQYxJ7)9H7D7IhM5n@ltYf% z-r)tW>fz4kFJ}YhL}4607d1XzzPuB(jzv{)-0TC9*!!O^ z92f=Ox3y#EFZ2L|*yfa|wYDEj|L}6w1rLE|WcpUu7zPMlXX0CJ-wzrE_jXJy9)LQL zi(gM?_e1E}3)V|w8Q{;j7vrGF0PoTZCp!ZBf%}WJK5r-`r>|smIpsw!=>2$~BtD$8oM1lnq#!SW2D$NsG^v%3WnZ72lcszn;DD!Sb1_YJ3mG zN%T2{Uu%S2@sSg+PEvY%HWfx|-_*lUR^${fe+M=G+I>Svj&h;#9seWMOv%dKQ)Xdo z7=-_?uQLzFYJc~?gdS6dM1zWoBr+r_kNrZ$XB1$MKWHpc!Whhic$W%nhkfD$! zktAu5%rlR9jI-{$y{~h==lrhU{%2p?)wEa7TI;^w@7L?YdG7sqRh}Nwvce!VfF5R_ zerCVMIMhc5{F=DGY7Wsu>u8#eLm$yOv!C_k+}{)_%V&yJmbH^cEC0+Z^k-qWr1Wvh z@Hg416z?_Y_<<;-{3$O>XrL#b;$R7_4q}vXFU;@OXL4JG@epRZ=-DR-%yfs=&EQN| zquh@vSIdI+J0Rt%w9WkkAy=JW4Tf3m8tNEkiJ5o(POYT6$)lH6m|9hdwqR-%wwD!C ztJR|x7*5rzOBzDe2Uow0}d<;nBXPVMz#&dfvDeLwK~y{$@1H?XG??IJd_o zu;bkBU}IGQSHiwyI0&vpQj%&oT#4`Xj>lIu_mM;~%1-7KeJ~j76Fq}~M$*+U$u1~A zO5zIN-AjErN`Cd|)p6=E$VG0lC`7OcMJ4J$)woUJu996kd8TAvBh-AEHvNvdP`LwId)pYD%N1|_VBV}Sy>N}nZK)W#I276eWW zlFbc-KiV%D;@ zKx*d8)8R86%~DpQZV*0)x>jKL%(bh2t^50SV&B!{&zN6Lv~qu*{g6PfXn~1A5K52c z@+v_nebgu!Lutx7Fmy(=k3<~lZOILwCmg}6CsaJ?^n2j}466^FFvYMs#lsxK>Nz$_ z_?*}j6^_pdD&guOd{Ep|UxHh3kY*Ha!HPjgaSM(Ynf+j?bsM?Fo3v#qy}=08+1Leg z4bwp24mIONbTV47Wwv3@IEj;1p?2>ZBN+|0nO^j3yg;^+PfT@!7!;QZjQ$!Y9BmHD z)(g8xQqbAGMUTgb)y?4EBCm0Z?Dt5~-#^kzD#Ug?4EZxmZ24Nwp9-dzNPp9m8ansS zxKgaKRbY?^uspc-pkau}8d(|{DbjB{<$2aTbeB78`^!DAcMK2-_1m@e-b}*jHI?B1 zs+a8gJ}$zbt5(zYRD4I|FDXxicf^+N(MEVjaNAp)H(W;zlTmf99346uO%9>#Cg@!M zMfC3sIjLU4=a(rH72ZcS#;wk--}jr;HyO3-@A^{tC&DG0K9E4GpJ3mV}< zHl5^v`y;;Oa5~kNsSo+?HbCa}@6ZTf4iNiyIf2&n>$rYuWO8+DA2A9JeIiuVO@8y) z#ivs`J!Ao2?)B$MjpQwd=c{<{--P4c9j$I#`Um!N4E>01AP?Ek!>B~2ly2dqWYf73 zC#A6m2QeQ!w`(irgKUC)pCKP~apb{TPO`@VYq|Obc0Ewb{cg#@`pvbj1nW1E%q$@a z>bFx@pJ2`A)MbV>oA0bu0Z_ATO%}yEEq8hk)@fN=ys=IjU{k@?Y?oG@CA4M_t_@*p zW@hvf+p*`VMz5e9laR8;c1$r=6>JenOE=xaiLUy~Y zWzjmwZlAG92E$nG%p-UJ#_FDxFL12hyu}HxkbPHzCS0MjUVHHhiTb=#NEGQI`A1Sr zzvhmR{Bs+%yLp+!^OJ?jaf?Cnypwy;y+xCRSM%M1J2In0Y~|7mEQ^Q91~$rx%YW4* zabUMGEk8O*)W`=FbKgOdv%!&H`sWC7JGy^uh}a~FeavLi{<;%Ld}1skLr39b&O_Mtv(-(@b;+LNlB+tH{Sd$z|Sk^`vFe z4db}xrR+fv$CXeeO_7kt70W#6u#W2`4AVgF{ex}v`Ca|r()3$MhAHzb6pPjU{LN4- zK5OQ}VlgOk9aF2Yl}L5@Mhw#L`{FNGuQ=!R5F}6Nvk@FN8Gb ztmr;-w}zZ`ewPhxcI$&x*k)VY=j?|z+uv?Q{#!;b*<|=K%YpHk6y-`iO1e+yeka5h zOwlFxv50mox&1|xvE=5RIk-nBrctAC73T^=`6dv z1(#0q?ep^j^XNzWg(G3FuF(B|V|1za^r|5;#s1!MciIFwn!Ty^2@8w5`}5K}_cx4@ z`RloT&NBu$Vwfx1@Pp)v9_Xs4{d#{hXeaV(=lYls}ZRNQu+;-tG?J?@jQ=@E=` zcF2v6UA^RfTl448-U0IR^VWV1%enbz^=eTw@WL_tET)|?I^r3-V^Yw_Mob)?M zUF47U-QNAAFa3{#ky8gzO4`Ypo!d`N#iy-yrhB^C6}K}#92p?_Y7!o$!~G=M2~$6zoKF7m^{ME^JMZWv6I zPsU^FcAvMD+6u9oG~Wu0-DG#Kz$&$Vqb*jczlKtpp-L_O{tNM$G1E(k&#;VGAwJV| zpmhgOGRzNNNXht(^49?+v$=U0(lGw54oJfk$R3IV8b(FX38|N>#y@uh^zy)%x`fnA z%Bn!5Ti)BXR0G`-ex9!z=#~Kfmo-pM=Qwt4fO2{n&qFMyqpn|1fXHH4@%&%cmm0y`=A2UI0_*ev&Rnb){3o~Qw>LJqUIx2I^V}kgu zE(~}YLl+4g4%#>9eL&x5t=EaMVfqxD`||qoG!tf8Dg9|fJE>~gm@HV_Lk}E+VQO^5 zGZrLMT|cXtP)$|$(_LtkDpIsFBgUPs+n&l}(^=h-*8@#w|LGbgG@V&rPp{p>>?Rv@ zD{aqJwUXwu8&qD0caw@`{mB2!G~^)vvm&B>0{9=1jBo9kn_J1M=Khqu^oKe_^}d&9 zT?Y|%{VWeCS7c)N42^Qp-!fUcY8kLxi`VWE`2M4oTsc-VzwL7qA-@MMl_|B6B(=-P zczG#|A>&o@V+t8B<+_tGe^?sH$93pK4a$wgU3(sZL5b1AOgiu)eL_R`cr`--A6BZ*?>I%1IvCt2^ zEeDjE@U~0{@kfN>ZA3dF6k;~%NKgbFp>Q?ReSHO2GncEfxSEB;IAig!o>v2lhfZz} zx_B5QVoFTL7-cw4)&gYlV>?8g=2Uuez8fc&%|F-v;Tj~d&W$OQrg<;v=y7!5^`Un+a{|v~(k{l6 z4Ugo+Bk}l1)|!&RwS4fQ_$aL9y~nrWTF%C2)%nY9jJzGyzgW^SLShQnPjRdD5i|Qo zK@W6#$@Nvc4|L};3BOmAnIrw#5}S_cjylstl-eDC^BK_J2SikdGCb4IB))&Ve_GR9 ztdz0xg^qIrf3n~OU^H|Z%P>|CUz&%p`cyf=Sbg@nX;rBn8ppk=Y~6Nb zl=wZVklyS$MtaPfF8ID?QD@BBsQGg46yZ4JQr$5GEb9B(SWCtCPtXaas9SCNCxyEU#hw`>w+B`s%$qF0WfZIANpmTQn&T8kJ)P zpKG8|F&}rQ`b1?$NV>K+w*tKyxoj2QkWc6P(f3qUhv`dRc_%)6#e4bzvhCXa4}o3u zF$8jlI|cg*+Z*$cMV13(vBss)4fOkds@Gnat8;*`=qvYL_&q?pIi9_j*+r*kv1#~r z-Rz#;3~$#jVnP(WUDM80V=|(+oGOZfXyo?sQy7i#zR$wm@3BG-_I?XW-(c_O6ucT) zuIJ`Qgn;GR$heFwm;GZiLmEE*l`v56 zWZ3*QLhmzFQiB;#@8y9WNIpxkxg+^}ILHafXWfr>h|SrqR7PyBG&~KkIpWe9fL*X+ zuk{<~f_+8Hu?xPG6Zjb7wNg7lh}Ve4#p{@^Wo$Txqnl;mhd(g7xuuNY=tkwUmg1!o z)hOwKOJ^~+1uxyTeQvug90th6Cz25&x9R6WwxC(-r2%rcDt1d@3zNv!8~VQAFi4)X zjwgIQI6xM*_4Hk_?j@T03M)Ko=z`K$CE8S+hE%vKR#aGZ6TKeIML!KY=-BT1f+gd< zL?nI-AXpR;inD731S_q){}CWq1$8$qPdaxIiGfWfCUh&-!`rIbM#t^+x76DPV6w~U z@t*)D$5-}_0+>9*DZZvcAg&d>uSsp$vkzCa~>d> ztNE`PHd8c{IaMW(S+7~*6U=(MIp$*4D?Cjh$q@D?14)M1+#g6Xl%^>h(zBT%)rL_* zJ?4{Si2k-#3DYAax1gahnK4UUDEzTff!idh`C7M4_rfGe?}}BGn#ZDU%{%<`?%Gju z;OE|l%AS*CM$hFV#a2B*%x#A`lVq7>d1Rs7g&F!6;=F=Sz@9an_ypvr*1#uV!|fW} z@nU(-;g0v+o_Yv7UXw9TKji#l4?FT9=TCn9AQf_cr=z`BI(0`#YNOwr{k!O1kfmxc zDx!}J1-QGJ$}q`jvkb+NyQiP5Oz4zOcV-ef`3)*LbLm3y)^j&AYZ@?M8NcXxdVuVc zr|_+nch>;lS}RQ)@ul_bf)?)gORE%dzaQQ_jQjn^Fn@&gc3HjQRC5%aJjEyS82s z81ryyR4LfXw~oj1~M%bxjS5y>BMNDGQci>TdLL~^IURfkZ?!mb%ZrC)+} zQxGZ%e_4YQRI&Qs2`a(tDo#*iR|W8jo-FgnE83X1>JnVhMO|rZ$^%S7nJ)3*^SSphoSmP7UAKFL8RsUNfT^J5?sfo}ICd`I1^`o_%Z&}!==g4XGt;DYc_%qrUamA>37$~E8C5r&hB?Z&z%=YQ zKbxM0>HU9Yt|^KKdKa$k0r8+F(gDQ-#Sh^aKwb*Dh5=*}BLoA;I|CDlSgsvcM#OSn z&oe|U(+k=4pcU_Gxq_|u5|t5b#mhH4>aQM}AlpN;4W|nR$kTv@e09Sk#5mAcsDd)2 zWAwP|eqk+kyhe_0KIKR6-<@QV>BNJ3Tj;reBWFB5x%O#iz6m#L zBgZs8;e+exG&gQSACw|XU=!jR&FX?pXlws!0|=jQ7TLK%_$=g*7Y^a`$pZIYAR@$n zq)JnOMD!Q0kOd^7jU^_-^Y#FFe*C+4NGwgo1sUcnxky*92kZBoZeS8FKW*{!Df+dV z&mLnmzn^#>H^g_u&Fmz6N4(O@+z;;vLtYtNUg=W+ad|ybDuv4{RlQahiih+Ycgmr6 zkhFM6Ve#N?+>KaG?ZbVD)hx}iL#$@8WfP*(>pij#FQiH7`!~ywl%{@cdj?vLlym2O z&~hpQOk6?Bk@K>W068sReKE>uFXv?31vzcQx*imcUbvc}ZmV1!1FaT+mMbPE0e=)R zG0A8Df{95B>pC1*=0&f?fo0b3FdSIS{yfDi+DeIU#!I?x*K{;oQtyRpu8V}yOla~# z$E0#5S*)@~W(O}F&D77S>|5VQ*n_)wtJBkLj-Z81UKEoIJe`vV&^PNjDe$3F z%f62vx|c!L_@Pt#l8yde=z@9Bt|qQA%9vlwSaOPvR&U}mg5-iR~f(-dQL#BOzM zMn_D#_9!}HCQZ_qioILdim8~?^;$^9C{hu31r3G(p>{MFVhZfhU>K@>hRx`{)-BkK zo?PpO&8S7JcqnjxvT0k8`U78C;1z<8-Wj>fU=50X^agWwc9?M!F(dxa0T;~)Wo#9 zvcv$xYL4v&Q+mX{MWq9(?`>knV;>yohw)?0Lg<8R;*48+&i#3(OK=;13|%nHX8;7v&v3jPVlCr5eJ9zMMdl|H?TAf;@prs?1PtD zE3vl&F7>>^wi!ZSYOBrqym1=`$u_T4v0zR*8?t3nKJP+*Q`wj!>j2ZOGNB3#fYWtJ zlVt*@+qTdZKP=B3E%C!*r^}BYmTv;~C>-gCtBQhfq_p}V3P-jx?l@XadG5-D(aPWa zD2`T@j5VlqTF?AdI!(Sj-+~rLcvN-J zii>eb-2?5I@Yj(PXvg0Amti|5uKO7I|Cf<%$p8O2En*D(|0m0pm4IyRWVwULmJ0K5 zEFfDRM$TS$Y1nn1w19rD0h5T%%VR74Ge94i6rYU`inO5Yi}0YxIG&3S3cr>GI9AU~ z4a2eedg=!ptLI;9{IJ?EP;!hq|UmFU8Qjv{lv@!^xMrEWVu=^ol&#s#DF?hjlrDBlQNFrLQ?KEdSk zXa^>rQo<#ee4buzho$v;wU5uCw6fT$r&%4S*<@<(dIV0VnEa0*a5|%#E84*6Y)d?302ZrG{W7#z6@Le7fW@j< z>)-+g)Z$aIXh1FhNzj0rYqs;^EgI`x7Ojj}ug7bHV!(P$V%?CGxRJIWNr{d2zv5LX z8kN{3&JqVP{7c(o9T3Bd$SPqBAAZ7n6Kug!LuL`M1sf=zjDan9XX-J0O%ER4OTlaU zf>v8LyrwtCZO6akcY&wy@A&AF1Ne8$&d!B4P40*Z+B7+t(#2rY$T%DpqAKVC#JBD5 z0CM9_A`T$)e*Qwqls`EdCDRUuDoUmnd)hESaIQ7O0AXLyEIr5%B7XRzFFaM`iN3Hv zs3Q8pot00qAt_6~jSY#seiJq%HoF{AW^20SD2mnksd7ZW;p;QnT5i^ThT|HZ?2f4guA;P5H&#?Tao&yA_8 z7s2o;BK9H@vLl|0;g}t5@}`C!KzP(XAS(j(7&F=l>oF+~(|oAMyvXG;Af)Uh(^-I! zV$n`OLQ4I06i&${BXWOWO5T3-98SsI&lG<^TE;TtfoYj?NgbwT>f1)KS7bkDfW2bP z+i>g^xhR)9WuVkfD_SF^<~jWgDK!bhuJ52`?Catw1~o(E+}WF;W(b*Ep}VPPmaqrZ z&6+i7sBUi6`&t6hdF!UVWgwmRUhv|tr=@f2_3P1w-KC}I3O4MT{Y7ZQZoXcNS`IZc z=&l7`PH|K(dO0ThQh5QHR1qyH0c7&fg5mRkOumY67l!tG<~$F!-_|Atv!MM}<6IUD zS+8UM63lw-bh9w)oquiycfW?J`?&jkY4ye3&vG9RzNQ5@3*j|Qk)-k^_@18eJhlQ< z3oGU~R4vw+%b{w~mz9EBE&pTI^{~}ev+`=fR?C*DjLvAG?j$;+B0*K?jCN0yVoEL% z{}xm7Q!_P~lC%9#nGZs877Ggs$(GRuw2(|O39l}j3Xlndx3!T>aBNitGJ%{*u}1YO zupk!ItNl89%R#-`8mf!5Y{mI*q-8HyNg^$qX>=M#v;yHs9MJ?0I^c*lsN8~qhME!6 z7y^xq)ngcFe0;g7Ba@;ll4ltaSds8mUc!pR)ag7zDb}S~2&GUH&k;&F?2@q^Na=~i z2a%LcicH7?QhHa<8;o9z8qGgK^kUe+hS5touN`(mRf(&y6H+?%w-c&-RE9Xu{dxKN z6!1JOSGADmnV+G8w0c;VHqz>^I`<&0-gM<53WzeQTN*$>wCLhM0dYaWBpcZ7uJwP) zz;<6=5rMY*(N}jfz!_yz7(r*$DaqdtoKc%cuVTQ27{1c63rq;Jj1US{2+=7cgv%tW zHKzeCyHich0$g??Ek7EX%bkTnp3q#r6VWh-=JLn!Lzo+`8eYu(y?MKZWkZ{n`SdWB*v7Iau4odP-h|6+mm(sZGD5*X=jO0FBCx>jNsNsaq#WwhburF^qBX)2bk{0gSN5;ez4kS+5SMQeeP&ITI~XFcRmB+>DLJtB%UU8 z6OefFz95cD=ZaQSR66}XXoY~%xkuO+G5q`uoK|KO&EeNJ@>Kwa?>m}+QE9Qx1V*La z_5&gil`5>U!s>NL;H@K2z2*+eD?|0FP|Aa}n`7y3q}>$1ZbRCQ>!CC%7}f^cP{B}e zzK05iQSce;f(_2ppf7mr4e3e9ncAXsMjs5r&e= z`85|%QW;VYMDch$)dt1mC(%z)JQfKVy$=rgBJFNPaL7FiEjhp;w|J%$2WHfPSLO@B zjG7U%O$0M4%1M^;1K3olYtxm@82MG%#8Oj#NA<1y>sM2O=JlxCL z4ngBF{f=u5G#<9{%}yXBpJAqevHA1Xwy*HShCx!!?=O=zvjKay;qtu>;p6@b#(*WB~ib_so5 z$`5T9m$VSJU8dhh5a+qqK_SkgC0>9y&q+shM7rlZ2|%Q~h#I-C2u$~sWGI3}8jCa# zBr?yNg&>i_mY)cvGu`I*0hDgBUkRb~>aGQ-Vvgm!Llx6gDjii!rBr!AII7|M#eO`h z!Gj|ak7_W=O+jthi(iZ}y0mVr(#O)e(*8S^)+KHyP{p*0(vSibQ&X~X6R4PnhsKZ-bU)v~ z3Y?(O$0={%1kHu|z=Wnqmy<85&}|8FU%-TJH{}`!d!>=*5ZWt6ws+B9`FZKXXCM{= zO5C3Suu#!^4S|I(HZ`okh#oj|1sPGdW$VL%5%r0YLvblps~W|nx|ydaF3k_gMmTkM z>k1l9rRX`mS_bLVuB%q4Ud3MzK=o>U+Ba0MJ`akaPCt{p9d&xH+%Z$o=`U6-M-Q-- zf88W_fU9o>?g0;Q@AF(ltL2g|AX#l*vbr3|>N1|y2p*|DnL~p|<7DeB32K^JHAId$ zZJP)BJ-?UpSJ3a%Ua_Kn&oaG=8{qTW1xFD+-zGa`3-I}>zK?f7tsK|TMzvDJ)u-1;Al91$hLAFRCy7%MIH{YJtsWUDUz=n=SE87s_mX{G1qT;O||#CW`)^nJWKN@b~`rX>%u2>GQw-J#B8q?7ydxZDVKr zJ#wsu!T9I2xx@6A@U%HsafUwMBM-K z=Y+YrJdFS633J From 1a3a03cf0c605ef5b6b66b600f64a659564e7761 Mon Sep 17 00:00:00 2001 From: jshipton Date: Thu, 18 Apr 2024 11:13:11 +0100 Subject: [PATCH 23/48] tighten tolerence in rexi shallow water test and some work on rexi boussinesq test --- .../test_linear_compressible_boussinesq.py | 61 +++++++++++++------ integration-tests/rexi/test_linear_sw.py | 9 ++- 2 files changed, 47 insertions(+), 23 deletions(-) diff --git a/integration-tests/rexi/test_linear_compressible_boussinesq.py b/integration-tests/rexi/test_linear_compressible_boussinesq.py index da381109e..7cdcbeebb 100644 --- a/integration-tests/rexi/test_linear_compressible_boussinesq.py +++ b/integration-tests/rexi/test_linear_compressible_boussinesq.py @@ -4,6 +4,7 @@ """ +from os.path import join, abspath, dirname from gusto import * from firedrake import (PeriodicIntervalMesh, ExtrudedMesh, sin, SpatialCoordinate, Function, pi) @@ -15,18 +16,19 @@ def run_rexi_linear_boussinesq(tmpdir): # ---------------------------------------------------------------------- # # Set up model objects # ---------------------------------------------------------------------- # - t_max = 1000 - dt = t_max - L = 3.0e5 # Domain length - H = 1.0e4 # Height position of the model top + tmax = 1000 + L = 1.0e3 # Domain length + H = 1.0e3 # Height position of the model top - columns = 300 # number of columns + columns = 10 # number of columns nlayers = 10 # horizontal layers # Domain + mesh_name = 'linear_boussinesq_mesh' m = PeriodicIntervalMesh(columns, L) - mesh = ExtrudedMesh(m, layers=nlayers, layer_height=H/nlayers) - domain = Domain(mesh, dt, 'CG', 1) + mesh = ExtrudedMesh(m, layers=nlayers, layer_height=H/nlayers, + name=mesh_name) + domain = Domain(mesh, tmax, 'CG', 1) # Equation parameters = BoussinesqParameters(cs=300) @@ -80,26 +82,49 @@ def run_rexi_linear_boussinesq(tmpdir): b1.assign(b1-b_b) rexi_output.write(u1, p1, b1) rexi = Rexi(eqns, RexiParameters()) - rexi.solve(U_expl, U_in, t_max) + rexi.solve(U_expl, U_in, tmax) u1, p1, b1 = U_expl.subfunctions p1 -= p_b b1 -= b_b rexi_output.write(u1, p1, b1) - return u1, p1, b1 + # Checkpointing + checkpoint_name = 'linear_sk_rexi_chkpt.h5' + new_path = join(abspath(dirname(__file__)), '..', f'data/{checkpoint_name}') + check_output = OutputParameters(dirname=tmpdir+"/linear_sk", + checkpoint_pickup_filename=new_path, + checkpoint=True) + check_mesh = pick_up_mesh(check_output, mesh_name) + check_domain = Domain(check_mesh, tmax, 'CG', 1) + check_eqn = ShallowWaterEquations(check_domain, parameters, fexpr=fexpr) + check_io = IO(check_domain, output=check_output) + check_stepper = Timestepper(check_eqn, RK4(check_domain), check_io) + check_stepper.io.pick_up_from_checkpoint(check_stepper.fields) + usoln = check_stepper.fields("u") + psoln = check_stepper.fields("p") + bsoln = check_stepper.fields("b") + + return usoln, psoln, bsoln, u1, p1, b1 def test_rexi_linear_boussinesq(tmpdir): dirname = str(tmpdir) - u, p, b = run_rexi_linear_boussinesq(dirname) + u, p, b, uexpl, pexpl, bexpl = run_rexi_linear_boussinesq(dirname) - for variable in ['u', 'b', 'p']: - new_variable = stepper.fields(variable) - check_variable = check_stepper.fields(variable) - diff_array = new_variable.dat.data - check_variable.dat.data - error = np.linalg.norm(diff_array) / np.linalg.norm(check_variable.dat.data) + udiff_arr = uexpl.dat.data - usoln.dat.data + pdiff_arr = pexpl.dat.data - psoln.dat.data + bdiff_arr = bexpl.dat.data - bsoln.dat.data + + uerror = np.linalg.norm(udiff_arr) / np.linalg.norm(usoln.dat.data) + perror = np.linalg.norm(pdiff_arr) / np.linalg.norm(psoln.dat.data) + berror = np.linalg.norm(bdiff_arr) / np.linalg.norm(bsoln.dat.data) + + # Slack values chosen to be robust to different platforms + assert uerror < 1e-10, \ + 'u values in REXI compressible boussinesq test do not match KGO values' + assert perror < 1e-10, \ + 'p values in REXI compressible boussinesq test do not match KGO values' + assert berror < 1e-10, \ + 'b values in REXI compressible boussinesq test do not match KGO values' - # Slack values chosen to be robust to different platforms - assert error < 1e-10, f'Values for {variable} in ' + \ - 'Incompressible test do not match KGO values' diff --git a/integration-tests/rexi/test_linear_sw.py b/integration-tests/rexi/test_linear_sw.py index 4e2e17018..f2a9e9001 100644 --- a/integration-tests/rexi/test_linear_sw.py +++ b/integration-tests/rexi/test_linear_sw.py @@ -16,7 +16,6 @@ def run_rexi_sw(tmpdir): # Parameters - dt = 0.001 tmax = 0.1 H = 1 f = 1 @@ -27,7 +26,7 @@ def run_rexi_sw(tmpdir): L = 1 n = 20 mesh = PeriodicUnitSquareMesh(n, n, name=mesh_name) - domain = Domain(mesh, dt, 'BDM', 1) + domain = Domain(mesh, tmax, 'BDM', 1) # Equation parameters = ShallowWaterParameters(H=H, g=g) @@ -66,7 +65,7 @@ def run_rexi_sw(tmpdir): checkpoint_pickup_filename=new_path, checkpoint=True) check_mesh = pick_up_mesh(check_output, mesh_name) - check_domain = Domain(check_mesh, dt, 'BDM', 1) + check_domain = Domain(check_mesh, tmax, 'BDM', 1) check_eqn = ShallowWaterEquations(check_domain, parameters, fexpr=fexpr) check_io = IO(check_domain, output=check_output) check_stepper = Timestepper(check_eqn, RK4(check_domain), check_io) @@ -89,5 +88,5 @@ def test_rexi_sw(tmpdir): uerror = np.linalg.norm(udiff_arr) / np.linalg.norm(usoln.dat.data) Derror = np.linalg.norm(Ddiff_arr) / np.linalg.norm(Dsoln.dat.data) - assert uerror < 0.04, 'u values in REXI linear shallow water wave test do not match KGO values' - assert Derror < 0.02, 'D values in REXI linear shallow water wave test do not match KGO values' + assert uerror < 1e-14, 'u values in REXI linear shallow water wave test do not match KGO values' + assert Derror < 1e-14, 'D values in REXI linear shallow water wave test do not match KGO values' From 346275263b8a3e5d7e3537ba48bd0d0e31bb29e1 Mon Sep 17 00:00:00 2001 From: jshipton Date: Thu, 18 Apr 2024 13:04:30 +0100 Subject: [PATCH 24/48] add perturbation field to test --- .../rexi/test_linear_compressible_boussinesq.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/integration-tests/rexi/test_linear_compressible_boussinesq.py b/integration-tests/rexi/test_linear_compressible_boussinesq.py index 7cdcbeebb..72820bc9f 100644 --- a/integration-tests/rexi/test_linear_compressible_boussinesq.py +++ b/integration-tests/rexi/test_linear_compressible_boussinesq.py @@ -96,8 +96,9 @@ def run_rexi_linear_boussinesq(tmpdir): checkpoint=True) check_mesh = pick_up_mesh(check_output, mesh_name) check_domain = Domain(check_mesh, tmax, 'CG', 1) - check_eqn = ShallowWaterEquations(check_domain, parameters, fexpr=fexpr) - check_io = IO(check_domain, output=check_output) + check_eqn = LinearBoussinesqEquations(domain, parameters) + check_io = IO(check_domain, output=check_output, + diagnostic_fields=[Perturbation('b')]) check_stepper = Timestepper(check_eqn, RK4(check_domain), check_io) check_stepper.io.pick_up_from_checkpoint(check_stepper.fields) usoln = check_stepper.fields("u") From 39a6db6699b0d00d40adc1559c783d9ba5fda0b1 Mon Sep 17 00:00:00 2001 From: jshipton Date: Thu, 18 Apr 2024 14:11:08 +0100 Subject: [PATCH 25/48] fix rexi compressible boussinesq test --- .../data/linear_sk_rexi_chkpt.h5 | Bin 0 -> 241006 bytes .../test_linear_compressible_boussinesq.py | 51 ++++++++---------- 2 files changed, 22 insertions(+), 29 deletions(-) create mode 100644 integration-tests/data/linear_sk_rexi_chkpt.h5 diff --git a/integration-tests/data/linear_sk_rexi_chkpt.h5 b/integration-tests/data/linear_sk_rexi_chkpt.h5 new file mode 100644 index 0000000000000000000000000000000000000000..9ab64658e4a93e8553fc9619ac5f1641406fde2d GIT binary patch literal 241006 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ÚU*C|c3s)um8~xmF zG@5VOuL(-CAKgJ6X9dl2YRh$s-ljN7J4h2)$Pi!%Fa#I^3;~8f8Arg);>#VR_k|(N z?Z=m+=`_x#?+7cIE{)UjF(uPQG|s16#FtE$#`*Lei6zseaX$TOQpt2_oKF{}l}wk$ z`E;#}lIhYopKg&^GF=$wvx!6kTtpNv8q#zcry;#B4Eb~^o_xAAPRqye?W5&soKKhH z$)`)>e7Y1*T7P26{z~!W)1`5~ycAFR{IrtgrFin`(l~7&#g|W);!Eojafh_ud|Zk* z?T3I4rRjV<((<%k!F`%eLuvUKu0PWIe4M6JJZU7el#Xe?g?LjuX?iKR()ww=G^F>1Ax)R!$=6TgG+l}( zpHAa6U5Y24PUAFPiYIMoAeI4w`{r0o~RDZaGbG*0tVe5L7b-1DXBJf6~Y z0p8Mf@O+fUrRDi{OUu)K3hvW%8q)F!B`#%VeYX*!M5klq)DG@ar}(`lThQ#@%pjnj0BCruZ|5uTi1a4JuK zJis(R!nb5PkFPY{PmnIa8{t`^JjGX9KBiB2b0w*h_66XMO|D@_;T&EqRg7vN3trRg+YM8}iI zm!>0pOSYHCm!=Exrufoy8mIMBd}%t3({zL;!E2@>6NBad}%tRSDH@orRkJjX*$K1rhh_bq<<7& znojAJmZ$jAbV{!@o#IQ=>HSYap?;!L^OEvhiZctpjzDR8_IAwA+Zz}O~_wC19 zJJMax$#%NNv}NBHuF;BY*v-n$f!hrHnFgooapfzaA6B4se&NFk&-gvYaTc4llf7i% zM<+}%a@-ZgPe}(?RJ>WfM)BU1UfkZ$x^{dW2{`AkK90|~X!hkUOE=4>O)Ra;lw{;Z z!W{q3hWU86Xi7SY==$eun2+~iL8gYJEZr zfnp=EG2Q2WG_OEYYQPnsM5b1}?X-2*xhec@O58Z_Pd(0CZ8@CGPVhv_a@=Bjl2HWe`pH$)PM$5DjOp1)QSyCqJB3_n${V zXbPgmx42S#&vK0He$QJn{UC4HVKPHI3WStt4Ik?2mZ}x1!S+T=IFs zE>qKv_520R9mCI`seF4We|3xGa?pGu`1sr*^p5Zezh|79C<$@+r?2wG<$vu*Q9d`7 z;wY52bdqk}PDfSfYKAvE#A!m4tkO-U6zpBN%5YWq3cuT-t=>7p!#94%t@eDFOCBCZ zb}$4O0t^9$07HNwz!3Oj5D;#)*RBWml3N-8bRBhI;Men&R=%v>{{`!j^I~oY%G7#f zGh2_8)hWXI#SmZ!Fa#I^3;~7!L*R=D{DSxBhW=&nJz5#ti+pkj^&doe@ku(nWC$<> z7y=9dh5$o=A;1v$y%8wQ1wd%;B+Cx){7~-YnA%zW-l(wFe=h>1*#Yhp*vl2$S@UiL z;$quyY-vrkMRcn^S?wrotG$N9xSFWTiY)rwlEmm&7S?4sy)^nA!SglR_n%F_LRYrh z(HtGpdC-ZEy9?Su+rvT*0cyu8#H-j2_4~HkqxgR=XW}&5bgMloFQMT-*lI5=j(aGs zO8PhYx!q{=FCB8sN5Ax`<~gObs&^^!C20psDaURz1Q-Gg0fqoWfFbbvA|QOfjp5g2 zzi*FzP0Q)~tpnL)KqT_#U!c@P7NkLPA`SG?s3Iy}OxzqUgQog-(K6F3L<)Hau`1sVb z^v;)`^Sj-p|64*FivJ%uInuXoZA8&2F8|;5hgRhWjJ8`C{{eq!w=AwQTou0hE&QQn z&9_$qxt0(oW;gY_&3x8gh5$o=A;1t|2rvW~0$)dfuD9qq%0836F@L!nT}Pdt{`GwH zy|dwBE+5Tp&&U0i(mP*%&hK{jbNxDp0NtNS_+BlH|G;|W-V6@HGPNFQP`~8J3vpuW zk&^hZ7(;*|zz|>vFa#I^41wPr0XdO!3H>|OUtZ(a3t4;zW$73ZdN$Az{3 z`w$T7FYWl^X@Yg9XK-LhaF8S@f>&@#<1M)Yvh}}K8|+7AOGd#Q;H%^nj#X#tfNo(a-F99 z?Va1do{zRabQzbA=AO#OxA6U<{~rhV`9kwO<>MOsJpXcQ{yTT6J%F%(#eAs}Z!gF5 zL!LKbzJz%R?Kj88a9rVw|I=wa9(4Q}U-|FQz<;qNrSX-Pf_X*hkJ8mY?F*d`!g>6? z^MQX&Kvt{jq)9^g_8)97leV54v@#{1wq^3^e>(M8d4>Q(fFZyTU8k{ z<3wH=e2)%h@6o>%-Lu9r1Q-Gg0fqoWfFZyT_@@Z`g7@g!2g~4l^knuP{ZC1em1hVr z1Q-Gg0fqoWfFbZ(AyC@B_riT`WZ6rfIM2NtQ@gm|3Ix{Je~f_84vX47ZV~K@RJ`9F z-7k-Ow8am$pfo$e+65dv_TlT5MZarrGWz|&At)2YN~7Nxp0CkP{%rac?hC4Mj-x~F zS0rl3$K3?&pzUEHhXA#o6yjBEKly$8-WU5nC~>yS{D8=bIL`Jj?0Y}rBKM3kweLL? z$d~B%0oZ#2o4E`Dh5$o=A;1tQy9fy1Z$tTYS=sIJztgjBR(1|*J8<1{qQ<-jaG1m^ z%-vri4wQuZii6N5_u`1)kl=t|pBOF{=pKUdS4U?8DTjX0k)Sv+lDiVcP0-7z5#Sdj zaSs!F1V@I4qs#D6Qs0D(MxTG;OIp1oIwCC6OX4*lL*uht6Ef<2c2C-{36X|Jw)obO zwHkl*fO{2YZ;AX!{b50At8wLfbY08=BHaC+rds?UUb5)2$s6toRJ!$pk731rQv`%` zsmJ$YHqWnUx4w1P2P0ZQM#%k(%JV*=BGKk@R6g$wjL?FlZ5q*A;1t|2rvW~0t^9$z!wns1@F=OlsFYpnUd=lvG?dNP#3$! z5MT%}1Q-Gg0fqoWpzI>h&Ca@qyv{hahTfl^`SNv0m*vXwJum+J40oj}&sjjJlRfs^ zRps7{jd@u=cURJxyStsQgS)$YFL!rsiM#EpDUf){yUEri?}|F`g?>0tepT2O8~)<9 zsEFRCaP_MxaTO{V;80GHsw`?3o85Xx26;yK1qX@4L)<+j;YI3PFL3@XYLAD>fRR>M z8jRJe5i21%Igv1j8gaLk6KRU7k#7xA3*|`f0I?)M5{SN&Bo32=gyDvWM72>3ctr5Y zJ(JVd(C7Q13!6tcrwD!3OC(ZijIZRR#yeF%?{K(c4Zf>9NVr6@>BhPkMkO6wvy@6i z>T;h~z#%67vQf0Sb5NL{r?14TGuIbAJ8QovdhIF+3J(r*4h!~*^o*c4t!$0;oY427 z41m6YmbGom;)tgtAfRK3GFIr0o?lReBrM83z(9*$>Feto$b3$Ts!Kfdd!D0jQ-wos zlZs0NIW))^uY3$4I^;!KBov`h;|}5Ib9eFgsb~&-M~VOTPVu-^D~4SX=ppe!hUDVN zFgq!Y`f?ThpMFc*0YPHVH7K?^7Yc1ZwfOc;=hJC(ztlOLMlms^8NV^)=mAlI{)BMD zyEA8-{k(zU)SkryBNzFQP9M^^=x=P|zmx6f^;3^Du(+R9J|CmgHKpVy68(P(PzE=q z;0k9O{Zo_8jq3`yv2@bWzX|dPS=<;4k|;MOU)46IE&IOEC*y8*X>;yzEp8MlYjUA1 zd;IkJTt1GFzpouE_wSEDX*{_FPd~JWLO+oxi6_~9w3S+%VJOMXz++LC0OXDwgJ87YqT007HNwzz|>vFa#I^zcT_tmzH%}Ute!gxq@Z$ zujl)nG5L|U{{mCI9)@LbJ(4b1@3ZyDk5D5k!w_HyFa#I^3;~7!L*Vy9U;}EdFA_l4 zMen!A(K!kksF@-`^h0#cLB@El$OsGP`IdaY;Jy5QyE6D*u5HbAic%7LFQ-VbkRiYj zUk?_?&7!jiuhOKUzI=c71?Ul{VuWYT#YLE z#JOfhv2`c4n*O@0NH2+JL~xj%u~xKJ2`>-o8DdR6L$G@YaSq`A9#x5H)S(1a-I!-Q;x(S2D~@)!(Chj?m@S99Y4&3u-eJlC+5kWh?vHCwI8#Ub*;#F*V|L=TrR0Y4sql1PD=v54l zorV@5ulFL6g{XA}QR^c9O{^U6kt;7JPli~9Ú|H7GPL8waU-&qy@5@*JK_99U< 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

M^o)5ER}GQpIQA+^+AM1c5A-+7KJGr*G?i^fd9 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 Date: Thu, 18 Apr 2024 14:27:47 +0100 Subject: [PATCH 26/48] argh lint! --- integration-tests/rexi/test_linear_compressible_boussinesq.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/integration-tests/rexi/test_linear_compressible_boussinesq.py b/integration-tests/rexi/test_linear_compressible_boussinesq.py index c39548351..a915893c4 100644 --- a/integration-tests/rexi/test_linear_compressible_boussinesq.py +++ b/integration-tests/rexi/test_linear_compressible_boussinesq.py @@ -7,7 +7,7 @@ from os.path import join, abspath, dirname from gusto import * from firedrake import (PeriodicIntervalMesh, ExtrudedMesh, - exp, SpatialCoordinate, Function, pi) + exp, SpatialCoordinate, Function) from firedrake.output import VTKFile @@ -121,4 +121,3 @@ def test_rexi_linear_boussinesq(tmpdir): 'p values in REXI compressible boussinesq test do not match KGO values' assert berror < 1e-14, \ 'b values in REXI compressible boussinesq test do not match KGO values' - From c4df6e342066fbc8812151e742ae39631aa54867 Mon Sep 17 00:00:00 2001 From: jshipton Date: Thu, 18 Apr 2024 15:49:55 +0100 Subject: [PATCH 27/48] test tolerence was a bit harsh --- .../rexi/test_linear_compressible_boussinesq.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/integration-tests/rexi/test_linear_compressible_boussinesq.py b/integration-tests/rexi/test_linear_compressible_boussinesq.py index a915893c4..89a3c85d7 100644 --- a/integration-tests/rexi/test_linear_compressible_boussinesq.py +++ b/integration-tests/rexi/test_linear_compressible_boussinesq.py @@ -115,9 +115,9 @@ def test_rexi_linear_boussinesq(tmpdir): perror = np.linalg.norm(pdiff_arr) / np.linalg.norm(p.dat.data) berror = np.linalg.norm(bdiff_arr) / np.linalg.norm(b.dat.data) - assert uerror < 1e-14, \ + assert uerror < 1e-10, \ 'u values in REXI compressible boussinesq test do not match KGO values' - assert perror < 1e-14, \ + assert perror < 1e-10, \ 'p values in REXI compressible boussinesq test do not match KGO values' - assert berror < 1e-14, \ + assert berror < 1e-10, \ 'b values in REXI compressible boussinesq test do not match KGO values' From e9475f1fe8b12c15027cbc6c26db9c63b487217f Mon Sep 17 00:00:00 2001 From: jshipton Date: Thu, 18 Apr 2024 17:19:04 +0100 Subject: [PATCH 28/48] parallel rexi test --- gusto/io.py | 10 +-- integration-tests/rexi/test_linear_sw.py | 86 ++++++++++++++++-------- 2 files changed, 62 insertions(+), 34 deletions(-) diff --git a/gusto/io.py b/gusto/io.py index 40352085f..c9ecffb2f 100644 --- a/gusto/io.py +++ b/gusto/io.py @@ -7,7 +7,7 @@ import time from gusto.diagnostics import Diagnostics, CourantNumber from gusto.meshes import get_flat_latlon_mesh -from firedrake import (Function, functionspaceimpl, Constant, +from firedrake import (Function, functionspaceimpl, Constant, COMM_WORLD, DumbCheckpoint, FILE_CREATE, FILE_READ, CheckpointFile) from firedrake.output import VTKFile from pyop2.mpi import MPI @@ -21,7 +21,7 @@ class GustoIOError(IOError): pass -def pick_up_mesh(output, mesh_name): +def pick_up_mesh(output, mesh_name, comm=COMM_WORLD): """ Picks up a checkpointed mesh. This must be the first step of any model being picked up from a checkpointing run. @@ -44,7 +44,7 @@ def pick_up_mesh(output, mesh_name): else: dumpdir = path.join("results", output.dirname) chkfile = path.join(dumpdir, "chkpt.h5") - with CheckpointFile(chkfile, 'r') as chk: + with CheckpointFile(chkfile, 'r', comm=comm) as chk: mesh = chk.load_mesh(mesh_name) if dumpdir: @@ -533,7 +533,7 @@ def setup_dump(self, state_fields, t, pick_up=False): if not pick_up: self.dump(state_fields, t, step=1) - def pick_up_from_checkpoint(self, state_fields): + def pick_up_from_checkpoint(self, state_fields, comm=COMM_WORLD): """ Picks up the model's variables from a checkpoint file. @@ -607,7 +607,7 @@ def pick_up_from_checkpoint(self, state_fields): step = chk.read_attribute("/", "step") else: - with CheckpointFile(chkfile, 'r') as chk: + with CheckpointFile(chkfile, 'r', comm) as chk: mesh = self.domain.mesh # Recover compulsory fields from the checkpoint for field_name in self.to_pick_up: diff --git a/integration-tests/rexi/test_linear_sw.py b/integration-tests/rexi/test_linear_sw.py index f2a9e9001..d7e8a9978 100644 --- a/integration-tests/rexi/test_linear_sw.py +++ b/integration-tests/rexi/test_linear_sw.py @@ -8,13 +8,14 @@ from gusto import * from gusto.rexi import * from firedrake import (PeriodicUnitSquareMesh, SpatialCoordinate, Constant, sin, - cos, pi, as_vector, Function) + cos, pi, as_vector, Function, COMM_WORLD, Ensemble) from firedrake.output import VTKFile import numpy as np +import pytest -def run_rexi_sw(tmpdir): +def run_rexi_sw(tmpdir, ensemble=None): # Parameters tmax = 0.1 H = 1 @@ -25,7 +26,22 @@ def run_rexi_sw(tmpdir): mesh_name = 'linear_sw_mesh' L = 1 n = 20 - mesh = PeriodicUnitSquareMesh(n, n, name=mesh_name) + if ensemble is not None: + comm = ensemble.comm + mesh = PeriodicUnitSquareMesh(n, n, name=mesh_name, comm=comm) + # REXI output + write_output = ensemble.ensemble_comm.rank == 0 + if ensemble.ensemble_comm.rank == 0: + rexi_output = VTKFile(str(tmpdir)+"/waves_sw/rexi.pvd", + comm=ensemble.comm) + + else: + comm = COMM_WORLD + mesh = PeriodicUnitSquareMesh(n, n, name=mesh_name) + # REXI output + rexi_output = VTKFile(str(tmpdir)+"/waves_sw/rexi.pvd") + write_output = True + domain = Domain(mesh, tmax, 'BDM', 1) # Equation @@ -33,9 +49,6 @@ def run_rexi_sw(tmpdir): fexpr = Constant(f) eqns = LinearShallowWaterEquations(domain, parameters, fexpr=fexpr) - # REXI output - rexi_output = VTKFile(str(tmpdir)+"/waves_sw/rexi.pvd") - # Initial conditions x, y = SpatialCoordinate(mesh) uexpr = as_vector([cos(8*pi*(x-L/2))*cos(2*pi*(y-L/2)), cos(4*pi*(x-L/2))*cos(4*pi*(y-L/2))]) @@ -47,46 +60,61 @@ def run_rexi_sw(tmpdir): u.project(uexpr) D.interpolate(Dexpr) - rexi_output.write(u, D) + if write_output: + rexi_output.write(u, D) # Compute exponential solution and write it out - rexi = Rexi(eqns, RexiParameters()) + rexi = Rexi(eqns, RexiParameters(), manager=ensemble) rexi.solve(Uexpl, U_in, tmax) uexpl, Dexpl = Uexpl.subfunctions u.assign(uexpl) D.assign(Dexpl) - rexi_output.write(u, D) + if write_output: + rexi_output.write(u, D) # Checkpointing - checkpoint_name = 'linear_sw_wave_rexi_chkpt.h5' - new_path = join(abspath(dirname(__file__)), '..', f'data/{checkpoint_name}') - check_output = OutputParameters(dirname=tmpdir+"/linear_sw_wave", - checkpoint_pickup_filename=new_path, - checkpoint=True) - check_mesh = pick_up_mesh(check_output, mesh_name) - check_domain = Domain(check_mesh, tmax, 'BDM', 1) - check_eqn = ShallowWaterEquations(check_domain, parameters, fexpr=fexpr) - check_io = IO(check_domain, output=check_output) - check_stepper = Timestepper(check_eqn, RK4(check_domain), check_io) - check_stepper.io.pick_up_from_checkpoint(check_stepper.fields) - usoln = check_stepper.fields("u") - Dsoln = check_stepper.fields("D") - - return usoln, Dsoln, uexpl, Dexpl + if write_output: + checkpoint_name = 'linear_sw_wave_rexi_chkpt.h5' + new_path = join(abspath(dirname(__file__)), '..', f'data/{checkpoint_name}') + check_output = OutputParameters(dirname=tmpdir+"/linear_sw_wave", + checkpoint_pickup_filename=new_path, + checkpoint=True) + check_mesh = pick_up_mesh(check_output, mesh_name, comm=comm) + check_domain = Domain(check_mesh, tmax, 'BDM', 1) + check_eqn = ShallowWaterEquations(check_domain, parameters, fexpr=fexpr) + check_io = IO(check_domain, output=check_output) + check_stepper = Timestepper(check_eqn, RK4(check_domain), check_io) + check_stepper.io.pick_up_from_checkpoint(check_stepper.fields, comm=comm) + usoln = check_stepper.fields("u") + Dsoln = check_stepper.fields("D") + + udiff_arr = uexpl.dat.data - usoln.dat.data + Ddiff_arr = Dexpl.dat.data - Dsoln.dat.data + + uerror = np.linalg.norm(udiff_arr) / np.linalg.norm(usoln.dat.data) + Derror = np.linalg.norm(Ddiff_arr) / np.linalg.norm(Dsoln.dat.data) + + return uerror, Derror def test_rexi_sw(tmpdir): dirname = str(tmpdir) - usoln, Dsoln, uexpl, Dexpl = run_rexi_sw(dirname) + uerror, Derror = run_rexi_sw(dirname) + + assert uerror < 1e-14, 'u values in REXI linear shallow water wave test do not match KGO values' + assert Derror < 1e-14, 'D values in REXI linear shallow water wave test do not match KGO values' + - udiff_arr = uexpl.dat.data - usoln.dat.data - Ddiff_arr = Dexpl.dat.data - Dsoln.dat.data +@pytest.mark.parallel(nprocs=2) +def test_parallel_rexi_sw(tmpdir): + + dirname = str(tmpdir) + ensemble = Ensemble(COMM_WORLD, 1) - uerror = np.linalg.norm(udiff_arr) / np.linalg.norm(usoln.dat.data) - Derror = np.linalg.norm(Ddiff_arr) / np.linalg.norm(Dsoln.dat.data) + uerror, Derror = run_rexi_sw(dirname, ensemble=ensemble) assert uerror < 1e-14, 'u values in REXI linear shallow water wave test do not match KGO values' assert Derror < 1e-14, 'D values in REXI linear shallow water wave test do not match KGO values' From 8848b5b4019f0df557aba948c51aae8f1b35275b Mon Sep 17 00:00:00 2001 From: Jack Betteridge Date: Mon, 7 Aug 2023 18:56:12 +0100 Subject: [PATCH 29/48] Move artifact upload to seperate step --- .github/workflows/build.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 057e800ec..749604089 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -46,3 +46,17 @@ jobs: --durations=100 \ --cov gusto \ -v unit-tests integration-tests examples + timeout-minutes: 60 + - name: Prepare logs + if: always() + run: | + mkdir logs + cd /tmp/pytest-of-firedrake/pytest-0/ + find . -name "*.log" -exec cp --parents {} /__w/gusto/gusto/logs/ \; + - name: Upload artifact + if: always() + uses: actions/upload-pages-artifact@v1 + with: + name: log-files + path: /__w/gusto/gusto/logs + retention-days: 1 From 31ba5469d26eae575cb7e17058ed91eecf8768af Mon Sep 17 00:00:00 2001 From: Jack Betteridge Date: Fri, 19 Apr 2024 12:52:18 +0100 Subject: [PATCH 30/48] Don't test for rank on write output --- integration-tests/rexi/test_linear_sw.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/integration-tests/rexi/test_linear_sw.py b/integration-tests/rexi/test_linear_sw.py index d7e8a9978..c7a29aab9 100644 --- a/integration-tests/rexi/test_linear_sw.py +++ b/integration-tests/rexi/test_linear_sw.py @@ -26,21 +26,21 @@ def run_rexi_sw(tmpdir, ensemble=None): mesh_name = 'linear_sw_mesh' L = 1 n = 20 + write_output = True if ensemble is not None: comm = ensemble.comm mesh = PeriodicUnitSquareMesh(n, n, name=mesh_name, comm=comm) # REXI output - write_output = ensemble.ensemble_comm.rank == 0 - if ensemble.ensemble_comm.rank == 0: - rexi_output = VTKFile(str(tmpdir)+"/waves_sw/rexi.pvd", - comm=ensemble.comm) + rexi_output = VTKFile( + str(tmpdir)+"/waves_sw/rexi.pvd", + comm=ensemble.comm + ) else: comm = COMM_WORLD mesh = PeriodicUnitSquareMesh(n, n, name=mesh_name) # REXI output rexi_output = VTKFile(str(tmpdir)+"/waves_sw/rexi.pvd") - write_output = True domain = Domain(mesh, tmax, 'BDM', 1) From 041694109b1a5e3218eb6b304967099cd1d95586 Mon Sep 17 00:00:00 2001 From: Josh Hope-Collins Date: Mon, 22 Jul 2024 14:59:29 +0100 Subject: [PATCH 31/48] rexi - cpx.FunctionSpace --- gusto/rexi/rexi.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/gusto/rexi/rexi.py b/gusto/rexi/rexi.py index 2cf7b2ae5..111369908 100644 --- a/gusto/rexi/rexi.py +++ b/gusto/rexi/rexi.py @@ -1,13 +1,14 @@ from gusto.rexi.rexi_coefficients import * from firedrake import Function, TrialFunctions, TestFunctions, \ Constant, DirichletBC, \ - LinearVariationalProblem, LinearVariationalSolver, MixedFunctionSpace + LinearVariationalProblem, LinearVariationalSolver from gusto.labels import time_derivative, prognostic, linearisation from firedrake.fml import ( Term, all_terms, drop, subject, replace_subject, replace_test_function, replace_trial_function ) from firedrake.formmanipulation import split_form +from asQ.complex_proxy import mixed as cpx NullTerm = Term(None) @@ -79,11 +80,8 @@ def __init__(self, equation, rexi_parameters, *, solver_parameters=None, # set up functions, problem and solver W_ = equation.function_space self.w_out = Function(W_) - spaces = [] - for i in range(len(W_)): - spaces.append(W_[i]) - spaces.append(W_[i]) - W = MixedFunctionSpace(spaces) + W = cpx.FunctionSpace(W_) + self.U0 = Function(W) self.w_sum = Function(W) self.w = Function(W) From e17d7fa459126ed91ad098239edff37f0d868c91 Mon Sep 17 00:00:00 2001 From: Josh Hope-Collins Date: Mon, 22 Jul 2024 15:14:20 +0100 Subject: [PATCH 32/48] rexi - cpx.get/set_real/imag --- gusto/rexi/rexi.py | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/gusto/rexi/rexi.py b/gusto/rexi/rexi.py index 111369908..74dc350a1 100644 --- a/gusto/rexi/rexi.py +++ b/gusto/rexi/rexi.py @@ -202,10 +202,7 @@ def solve(self, x_out, x_in, dt): # assign tau and U0 and initialise solution to 0. self.tau.assign(dt) - Uin = x_in.subfunctions - U0 = self.U0.subfunctions - for i in range(len(Uin)): - U0[2*i].assign(Uin[i]) + cpx.set_real(self.U0, x_in) self.w_.assign(0.) w_ = self.w_.subfunctions w = self.w.subfunctions @@ -216,7 +213,7 @@ def solve(self, x_out, x_in, dt): self.ar.assign(self.alpha[j].real) self.ai.assign(self.alpha[j].imag) self.solver.solve() - for k in range(len(Uin)): + for k in range(len(x_in.subfunctions)): wk = w_[2*k] wk += Constant(self.beta[j].real)*w[2*k] - Constant(self.beta[j].imag)*w[2*k+1] @@ -226,9 +223,5 @@ def solve(self, x_out, x_in, dt): else: self.w_sum.assign(self.w_) - w_sum = self.w_sum.subfunctions - w_out = self.w_out.subfunctions - for i in range(len(w_out)): - w_out[i].assign(w_sum[2*i]) - - x_out.assign(self.w_out) + cpx.get_real(self.w_sum, self.w_out) + cpx.get_real(self.w_sum, x_out) From e5a9ef79470d3d0bfdc32683bcdab4183077d0a5 Mon Sep 17 00:00:00 2001 From: Josh Hope-Collins Date: Mon, 22 Jul 2024 15:47:38 +0100 Subject: [PATCH 33/48] rexi - only allreduce the real components --- gusto/rexi/rexi.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/gusto/rexi/rexi.py b/gusto/rexi/rexi.py index 74dc350a1..946da0ee3 100644 --- a/gusto/rexi/rexi.py +++ b/gusto/rexi/rexi.py @@ -83,7 +83,6 @@ def __init__(self, equation, rexi_parameters, *, solver_parameters=None, W = cpx.FunctionSpace(W_) self.U0 = Function(W) - self.w_sum = Function(W) self.w = Function(W) self.w_ = Function(W) tests = TestFunctions(W) @@ -218,10 +217,9 @@ def solve(self, x_out, x_in, dt): wk += Constant(self.beta[j].real)*w[2*k] - Constant(self.beta[j].imag)*w[2*k+1] # in parallel we have to accumulate the sum over all processes + cpx.get_real(self.w_, self.w_out) if self.manager is not None: - self.manager.allreduce(self.w_, self.w_sum) + self.manager.allreduce(self.w_out, x_out) + self.w_out.assign(x_out) else: - self.w_sum.assign(self.w_) - - cpx.get_real(self.w_sum, self.w_out) - cpx.get_real(self.w_sum, x_out) + x_out.assign(self.w_out) From e3d4e78969dae5a7409f88e618e1b460a96da6b0 Mon Sep 17 00:00:00 2001 From: Josh Hope-Collins Date: Mon, 22 Jul 2024 16:12:31 +0100 Subject: [PATCH 34/48] rexi - use cpx for accumulating the sum --- gusto/rexi/rexi.py | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/gusto/rexi/rexi.py b/gusto/rexi/rexi.py index 946da0ee3..01a1e1f55 100644 --- a/gusto/rexi/rexi.py +++ b/gusto/rexi/rexi.py @@ -72,19 +72,20 @@ def __init__(self, equation, rexi_parameters, *, solver_parameters=None, self.N = m self.idx = rank*m + p - # set dummy constants for tau and A_i + # set dummy constants for tau and A_i and Beta + self.br = Constant(1.) + self.bi = Constant(1.) self.ar = Constant(1.) self.ai = Constant(1.) self.tau = Constant(1.) # set up functions, problem and solver W_ = equation.function_space - self.w_out = Function(W_) W = cpx.FunctionSpace(W_) - self.U0 = Function(W) + self.U0 = Function(W).assign(0) self.w = Function(W) - self.w_ = Function(W) + self.wrk = Function(W_) tests = TestFunctions(W) trials = TrialFunctions(W) tests_r = tests[::2] @@ -202,9 +203,7 @@ def solve(self, x_out, x_in, dt): # assign tau and U0 and initialise solution to 0. self.tau.assign(dt) cpx.set_real(self.U0, x_in) - self.w_.assign(0.) - w_ = self.w_.subfunctions - w = self.w.subfunctions + x_out.assign(0.) # loop over solvers, assigning a_i, solving and accumulating the sum for i in range(self.N): @@ -212,14 +211,17 @@ def solve(self, x_out, x_in, dt): self.ar.assign(self.alpha[j].real) self.ai.assign(self.alpha[j].imag) self.solver.solve() - for k in range(len(x_in.subfunctions)): - wk = w_[2*k] - wk += Constant(self.beta[j].real)*w[2*k] - Constant(self.beta[j].imag)*w[2*k+1] + + self.br.assign(self.beta[j].real) + self.bi.assign(self.beta[j].imag) + + cpx.get_real(self.w, self.wrk) + x_out += self.br*self.wrk + + cpx.get_imag(self.w, self.wrk) + x_out -= self.bi*self.wrk # in parallel we have to accumulate the sum over all processes - cpx.get_real(self.w_, self.w_out) if self.manager is not None: - self.manager.allreduce(self.w_out, x_out) - self.w_out.assign(x_out) - else: - x_out.assign(self.w_out) + self.wrk.assign(x_out) + self.manager.allreduce(self.wrk, x_out) From c1a74844884a24bbc3d0dd2b82e550641dad1c74 Mon Sep 17 00:00:00 2001 From: Josh Hope-Collins Date: Mon, 22 Jul 2024 17:00:32 +0100 Subject: [PATCH 35/48] rexi - use cpx.DirichletBC --- gusto/rexi/rexi.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/gusto/rexi/rexi.py b/gusto/rexi/rexi.py index 01a1e1f55..20df64d03 100644 --- a/gusto/rexi/rexi.py +++ b/gusto/rexi/rexi.py @@ -168,13 +168,15 @@ def __init__(self, equation, rexi_parameters, *, solver_parameters=None, else: aP = None - # Boundary conditions (assumes extruded mesh) - # BCs are declared for the plain velocity space. As we need them in - # extended mixed problem, we replicate the BCs but for subspace of W - bcs = [] - for bc in equation.bcs['u']: - bcs.append(DirichletBC(W.sub(0), bc.function_arg, bc.sub_domain)) - bcs.append(DirichletBC(W.sub(1), bc.function_arg, bc.sub_domain)) + # BCs are declared for the plain velocity space. + # First we need to transfer the velocity boundary conditions to the + # velocity component of the mixed space. + uidx = equation.field_names.index('u') + ubcs = (DirichletBC(W_.sub(uidx), bc.function_arg, bc.sub_domain) + for bc in equation.bcs['u']) + + # now we can transfer the velocity boundary conditions to the complex space + bcs = tuple(cb for bc in ubcs for cb in cpx.DirichletBC(W, W_, bc)) rexi_prob = LinearVariationalProblem(a.form, L.form, self.w, aP=aP, bcs=bcs, From c5c827ffd593acd25033abcba29e191e4434dec4 Mon Sep 17 00:00:00 2001 From: Josh Hope-Collins Date: Mon, 22 Jul 2024 17:07:35 +0100 Subject: [PATCH 36/48] rexi - use cpx.split --- gusto/rexi/rexi.py | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/gusto/rexi/rexi.py b/gusto/rexi/rexi.py index 20df64d03..83487d013 100644 --- a/gusto/rexi/rexi.py +++ b/gusto/rexi/rexi.py @@ -1,5 +1,5 @@ from gusto.rexi.rexi_coefficients import * -from firedrake import Function, TrialFunctions, TestFunctions, \ +from firedrake import Function, TrialFunction, TestFunction, \ Constant, DirichletBC, \ LinearVariationalProblem, LinearVariationalSolver from gusto.labels import time_derivative, prognostic, linearisation @@ -86,12 +86,16 @@ def __init__(self, equation, rexi_parameters, *, solver_parameters=None, self.U0 = Function(W).assign(0) self.w = Function(W) self.wrk = Function(W_) - tests = TestFunctions(W) - trials = TrialFunctions(W) - tests_r = tests[::2] - tests_i = tests[1::2] - trials_r = trials[::2] - trials_i = trials[1::2] + + test = TestFunction(W) + trial = TrialFunction(W) + tests_r = cpx.split(test, cpx.re) + tests_i = cpx.split(test, cpx.im) + trials_r = cpx.split(trial, cpx.re) + trials_i = cpx.split(trial, cpx.im) + + U0r = cpx.subfunctions(self.U0, cpx.re) + U0i = cpx.subfunctions(self.U0, cpx.im) ar, ai = self.ar, self.ai a = NullTerm @@ -119,8 +123,8 @@ def __init__(self, equation, rexi_parameters, *, solver_parameters=None, ) L += ( - m.label_map(all_terms, replace_subject(self.U0.subfunctions[2*i], i)) - + m.label_map(all_terms, replace_subject(self.U0.subfunctions[2*i+1], old_idx=i)) + m.label_map(all_terms, replace_subject(U0r[i], i)) + + m.label_map(all_terms, replace_subject(U0i[i], old_idx=i)) ) m = mass_form.label_map( @@ -135,9 +139,9 @@ def __init__(self, equation, rexi_parameters, *, solver_parameters=None, L += ( m.label_map(all_terms, - replace_subject(self.U0.subfunctions[2*i], i)) + replace_subject(U0r[i], i)) - m.label_map(all_terms, - replace_subject(self.U0.subfunctions[2*i+1], i)) + replace_subject(U0i[i], i)) ) L_form = ith_res.label_map( From d574a3755eb6e9b6ce3df2527b66bfcc96531835 Mon Sep 17 00:00:00 2001 From: Josh Hope-Collins Date: Tue, 23 Jul 2024 11:19:53 +0100 Subject: [PATCH 37/48] rename rexi rhs for clarity --- gusto/rexi/rexi.py | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/gusto/rexi/rexi.py b/gusto/rexi/rexi.py index 83487d013..7f1cadb78 100644 --- a/gusto/rexi/rexi.py +++ b/gusto/rexi/rexi.py @@ -95,11 +95,11 @@ def __init__(self, equation, rexi_parameters, *, solver_parameters=None, trials_i = cpx.split(trial, cpx.im) U0r = cpx.subfunctions(self.U0, cpx.re) - U0i = cpx.subfunctions(self.U0, cpx.im) ar, ai = self.ar, self.ai a = NullTerm - L = NullTerm + b = NullTerm + for i in range(len(W_)): ith_res = residual.label_map( lambda t: t.get(prognostic) == equation.field_names[i], @@ -122,10 +122,7 @@ def __init__(self, equation, rexi_parameters, *, solver_parameters=None, replace_subject(trials_i[i], old_idx=i)) ) - L += ( - m.label_map(all_terms, replace_subject(U0r[i], i)) - + m.label_map(all_terms, replace_subject(U0i[i], old_idx=i)) - ) + b += m.label_map(all_terms, replace_subject(U0r[i], i)) m = mass_form.label_map( all_terms, @@ -137,12 +134,7 @@ def __init__(self, equation, rexi_parameters, *, solver_parameters=None, replace_subject(trials_i[i], old_idx=i)) ) - L += ( - m.label_map(all_terms, - replace_subject(U0r[i], i)) - - m.label_map(all_terms, - replace_subject(U0i[i], i)) - ) + b += m.label_map(all_terms, replace_subject(U0r[i], i)) L_form = ith_res.label_map( lambda t: t.has_label(time_derivative), @@ -165,7 +157,7 @@ def __init__(self, equation, rexi_parameters, *, solver_parameters=None, replace_subject(trials_i)) a = a.label_map(lambda t: t is NullTerm, drop) - L = L.label_map(lambda t: t is NullTerm, drop) + b = b.label_map(lambda t: t is NullTerm, drop) if hasattr(equation, "aP"): aP = equation.aP(trial, self.ai, self.tau) @@ -182,7 +174,7 @@ def __init__(self, equation, rexi_parameters, *, solver_parameters=None, # now we can transfer the velocity boundary conditions to the complex space bcs = tuple(cb for bc in ubcs for cb in cpx.DirichletBC(W, W_, bc)) - rexi_prob = LinearVariationalProblem(a.form, L.form, self.w, aP=aP, + rexi_prob = LinearVariationalProblem(a.form, b.form, self.w, aP=aP, bcs=bcs, constant_jacobian=False) @@ -216,11 +208,12 @@ def solve(self, x_out, x_in, dt): j = self.idx + i self.ar.assign(self.alpha[j].real) self.ai.assign(self.alpha[j].imag) - self.solver.solve() self.br.assign(self.beta[j].real) self.bi.assign(self.beta[j].imag) + self.solver.solve() + cpx.get_real(self.w, self.wrk) x_out += self.br*self.wrk From c93dcd58f8a3b722a1eea32c1dbfff5b13362435 Mon Sep 17 00:00:00 2001 From: Josh Hope-Collins Date: Tue, 23 Jul 2024 11:55:54 +0100 Subject: [PATCH 38/48] rexi - simplify building the complex system --- gusto/rexi/rexi.py | 51 ++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/gusto/rexi/rexi.py b/gusto/rexi/rexi.py index 7f1cadb78..fd5203955 100644 --- a/gusto/rexi/rexi.py +++ b/gusto/rexi/rexi.py @@ -8,7 +8,7 @@ replace_subject, replace_test_function, replace_trial_function ) from firedrake.formmanipulation import split_form -from asQ.complex_proxy import mixed as cpx +from asQ.complex_proxy import vector as cpx NullTerm = Term(None) @@ -101,6 +101,7 @@ def __init__(self, equation, rexi_parameters, *, solver_parameters=None, b = NullTerm for i in range(len(W_)): + # residual only for prognostic i ith_res = residual.label_map( lambda t: t.get(prognostic) == equation.field_names[i], lambda t: Term( @@ -108,34 +109,20 @@ def __init__(self, equation, rexi_parameters, *, solver_parameters=None, t.labels), map_if_false=drop) + # mass matrix terms for real/imaginary components mass_form = ith_res.label_map( lambda t: t.has_label(time_derivative), map_if_false=drop) - m = mass_form.label_map( + mr = mass_form.label_map( all_terms, replace_test_function(tests_r[i])) - a += ( - (ar + ai) * m.label_map(all_terms, - replace_subject(trials_r[i], old_idx=i)) - + (ar - ai) * m.label_map(all_terms, - replace_subject(trials_i[i], old_idx=i)) - ) - b += m.label_map(all_terms, replace_subject(U0r[i], i)) - - m = mass_form.label_map( + mi = mass_form.label_map( all_terms, replace_test_function(tests_i[i])) - a += ( - (ar - ai) * m.label_map(all_terms, - replace_subject(trials_r[i], old_idx=i)) - + (-ar - ai) * m.label_map(all_terms, - replace_subject(trials_i[i], old_idx=i)) - ) - - b += m.label_map(all_terms, replace_subject(U0r[i], i)) + # linear operator for real/imaginary components L_form = ith_res.label_map( lambda t: t.has_label(time_derivative), drop) @@ -143,18 +130,28 @@ def __init__(self, equation, rexi_parameters, *, solver_parameters=None, Lr = L_form.label_map( all_terms, replace_test_function(tests_r[i])) - a -= self.tau * Lr.label_map(all_terms, - replace_subject(trials_r)) - a -= self.tau * Lr.label_map(all_terms, - replace_subject(trials_i)) Li = L_form.label_map( all_terms, replace_test_function(tests_i[i])) - a -= self.tau * Li.label_map(all_terms, - replace_subject(trials_r)) - a += self.tau * Li.label_map(all_terms, - replace_subject(trials_i)) + + # real matrix M multiplied by complex number (ar + i*ai) + a += ( + + ar * mr.label_map(all_terms, replace_subject(trials_r[i], old_idx=i)) + - ai * mr.label_map(all_terms, replace_subject(trials_i[i], old_idx=i)) + ) + + a += ( + + ai * mi.label_map(all_terms, replace_subject(trials_r[i], old_idx=i)) + + ar * mi.label_map(all_terms, replace_subject(trials_i[i], old_idx=i)) + ) + + # real matrix M multiplied by real number tau + a -= self.tau * Lr.label_map(all_terms, replace_subject(trials_r)) + a -= self.tau * Li.label_map(all_terms, replace_subject(trials_i)) + + # right hand side only has real valued component + b += mr.label_map(all_terms, replace_subject(U0r[i], i)) a = a.label_map(lambda t: t is NullTerm, drop) b = b.label_map(lambda t: t is NullTerm, drop) From df86638db25ba62e50665475f419d7cb7588af78 Mon Sep 17 00:00:00 2001 From: Josh Hope-Collins Date: Tue, 23 Jul 2024 12:25:01 +0100 Subject: [PATCH 39/48] rexi - calculate alpha*M and rhs with cpx. Still have to refactor tau*L --- gusto/rexi/rexi.py | 77 ++++++++++++++++++++++++++++------------------ 1 file changed, 47 insertions(+), 30 deletions(-) diff --git a/gusto/rexi/rexi.py b/gusto/rexi/rexi.py index fd5203955..d56a840c9 100644 --- a/gusto/rexi/rexi.py +++ b/gusto/rexi/rexi.py @@ -75,8 +75,8 @@ def __init__(self, equation, rexi_parameters, *, solver_parameters=None, # set dummy constants for tau and A_i and Beta self.br = Constant(1.) self.bi = Constant(1.) - self.ar = Constant(1.) - self.ai = Constant(1.) + # self.ar = Constant(1.) + # self.ai = Constant(1.) self.tau = Constant(1.) # set up functions, problem and solver @@ -96,10 +96,54 @@ def __init__(self, equation, rexi_parameters, *, solver_parameters=None, U0r = cpx.subfunctions(self.U0, cpx.re) - ar, ai = self.ar, self.ai a = NullTerm b = NullTerm + ncpts = len(W_) + + mass = residual.label_map( + lambda t: t.has_label(time_derivative), + map_if_false=drop) + + function = residual.label_map( + lambda t: t.has_label(time_derivative), + map_if_true=drop) + + def form_mass(*trials_and_tests): + trials = trials_and_tests[:ncpts] + tests = trials_and_tests[ncpts:] + m = mass.label_map( + all_terms, + replace_test_function(tests)) + m = m.label_map( + all_terms, + replace_subject(trials)) + return m + + def form_function(*trials_and_tests): + trials = trials_and_tests[:ncpts] + tests = trials_and_tests[ncpts:] + f = function.label_map( + all_terms, + replace_test_function(tests)) + f = f.label_map( + all_terms, + replace_subject(trials)) + return f + + def form_rhs(*tests): + rhs = mass.label_map( + all_terms, + replace_test_function(tests)) + rhs = rhs.label_map( + all_terms, + replace_subject(U0r)) + return rhs + + a, self.ar, self.ai = cpx.BilinearForm(W, 1, form_mass, return_z=True) + + b = cpx.LinearForm(W, 1, form_rhs) + for i in range(len(W_)): # residual only for prognostic i ith_res = residual.label_map( @@ -109,19 +153,6 @@ def __init__(self, equation, rexi_parameters, *, solver_parameters=None, t.labels), map_if_false=drop) - # mass matrix terms for real/imaginary components - mass_form = ith_res.label_map( - lambda t: t.has_label(time_derivative), - map_if_false=drop) - - mr = mass_form.label_map( - all_terms, - replace_test_function(tests_r[i])) - - mi = mass_form.label_map( - all_terms, - replace_test_function(tests_i[i])) - # linear operator for real/imaginary components L_form = ith_res.label_map( lambda t: t.has_label(time_derivative), @@ -135,24 +166,10 @@ def __init__(self, equation, rexi_parameters, *, solver_parameters=None, all_terms, replace_test_function(tests_i[i])) - # real matrix M multiplied by complex number (ar + i*ai) - a += ( - + ar * mr.label_map(all_terms, replace_subject(trials_r[i], old_idx=i)) - - ai * mr.label_map(all_terms, replace_subject(trials_i[i], old_idx=i)) - ) - - a += ( - + ai * mi.label_map(all_terms, replace_subject(trials_r[i], old_idx=i)) - + ar * mi.label_map(all_terms, replace_subject(trials_i[i], old_idx=i)) - ) - # real matrix M multiplied by real number tau a -= self.tau * Lr.label_map(all_terms, replace_subject(trials_r)) a -= self.tau * Li.label_map(all_terms, replace_subject(trials_i)) - # right hand side only has real valued component - b += mr.label_map(all_terms, replace_subject(U0r[i], i)) - a = a.label_map(lambda t: t is NullTerm, drop) b = b.label_map(lambda t: t is NullTerm, drop) From f8b1fd67cb51eade94e69e944f88db134042a13b Mon Sep 17 00:00:00 2001 From: Josh Hope-Collins Date: Tue, 23 Jul 2024 15:20:22 +0100 Subject: [PATCH 40/48] rexi - calculate tau*L with cpx. --- gusto/rexi/rexi.py | 119 ++++++++++++++++++--------------------------- 1 file changed, 48 insertions(+), 71 deletions(-) diff --git a/gusto/rexi/rexi.py b/gusto/rexi/rexi.py index d56a840c9..aa271e0d6 100644 --- a/gusto/rexi/rexi.py +++ b/gusto/rexi/rexi.py @@ -1,6 +1,5 @@ from gusto.rexi.rexi_coefficients import * -from firedrake import Function, TrialFunction, TestFunction, \ - Constant, DirichletBC, \ +from firedrake import Function, DirichletBC, \ LinearVariationalProblem, LinearVariationalSolver from gusto.labels import time_derivative, prognostic, linearisation from firedrake.fml import ( @@ -10,7 +9,6 @@ from firedrake.formmanipulation import split_form from asQ.complex_proxy import vector as cpx - NullTerm = Term(None) @@ -72,35 +70,17 @@ def __init__(self, equation, rexi_parameters, *, solver_parameters=None, self.N = m self.idx = rank*m + p - # set dummy constants for tau and A_i and Beta - self.br = Constant(1.) - self.bi = Constant(1.) - # self.ar = Constant(1.) - # self.ai = Constant(1.) - self.tau = Constant(1.) - - # set up functions, problem and solver + # set up complex function space W_ = equation.function_space W = cpx.FunctionSpace(W_) - self.U0 = Function(W).assign(0) - self.w = Function(W) - self.wrk = Function(W_) - - test = TestFunction(W) - trial = TrialFunction(W) - tests_r = cpx.split(test, cpx.re) - tests_i = cpx.split(test, cpx.im) - trials_r = cpx.split(trial, cpx.re) - trials_i = cpx.split(trial, cpx.im) - - U0r = cpx.subfunctions(self.U0, cpx.re) - - a = NullTerm - b = NullTerm + self.U0 = Function(W_) # right hand side function + self.w = Function(W) # solution + self.wrk = Function(W_) # working buffer ncpts = len(W_) + # split equation into mass matrix and linear operator mass = residual.label_map( lambda t: t.has_label(time_derivative), map_if_false=drop) @@ -109,6 +89,7 @@ def __init__(self, equation, rexi_parameters, *, solver_parameters=None, lambda t: t.has_label(time_derivative), map_if_true=drop) + # generate ufl for mass matrix over given trial/tests def form_mass(*trials_and_tests): trials = trials_and_tests[:ncpts] tests = trials_and_tests[ncpts:] @@ -120,58 +101,53 @@ def form_mass(*trials_and_tests): replace_subject(trials)) return m + # generate ufl for linear operator over given trial/tests def form_function(*trials_and_tests): trials = trials_and_tests[:ncpts] tests = trials_and_tests[ncpts:] - f = function.label_map( - all_terms, - replace_test_function(tests)) - f = f.label_map( - all_terms, - replace_subject(trials)) + + f = NullTerm + for i in range(ncpts): + fi = function.label_map( + lambda t: t.get(prognostic) == equation.field_names[i], + lambda t: Term( + split_form(t.form)[i].form, + t.labels), + map_if_false=drop) + + fi = fi.label_map( + all_terms, + replace_test_function(tests[i])) + + fi = fi.label_map( + all_terms, + replace_subject(trials)) + + f += fi + f = f.label_map(lambda t: t is NullTerm, drop) return f + # generate ufl for right hand side over given trial/tests def form_rhs(*tests): rhs = mass.label_map( all_terms, replace_test_function(tests)) rhs = rhs.label_map( all_terms, - replace_subject(U0r)) + replace_subject(self.U0)) return rhs - a, self.ar, self.ai = cpx.BilinearForm(W, 1, form_mass, return_z=True) - - b = cpx.LinearForm(W, 1, form_rhs) - - for i in range(len(W_)): - # residual only for prognostic i - ith_res = residual.label_map( - lambda t: t.get(prognostic) == equation.field_names[i], - lambda t: Term( - split_form(t.form)[i].form, - t.labels), - map_if_false=drop) - - # linear operator for real/imaginary components - L_form = ith_res.label_map( - lambda t: t.has_label(time_derivative), - drop) - - Lr = L_form.label_map( - all_terms, - replace_test_function(tests_r[i])) - - Li = L_form.label_map( - all_terms, - replace_test_function(tests_i[i])) + # complex Constants for alpha and beta values + self.ac = cpx.ComplexConstant(1) + self.bc = cpx.ComplexConstant(1) - # real matrix M multiplied by real number tau - a -= self.tau * Lr.label_map(all_terms, replace_subject(trials_r)) - a -= self.tau * Li.label_map(all_terms, replace_subject(trials_i)) + # alpha*M and tau*L + aM = cpx.BilinearForm(W, self.ac, form_mass) + aL, self.tau, _ = cpx.BilinearForm(W, 1, form_function, return_z=True) + a = aM - aL - a = a.label_map(lambda t: t is NullTerm, drop) - b = b.label_map(lambda t: t is NullTerm, drop) + # right hand side is just U0 + b = cpx.LinearForm(W, 1, form_rhs) if hasattr(equation, "aP"): aP = equation.aP(trial, self.ai, self.tau) @@ -207,32 +183,33 @@ def solve(self, x_out, x_in, dt): multiplies by the corresponding B_n and sums over n. - :arg U0: the mixed function on the rhs. + :arg x_in: the mixed function on the rhs. :arg dt: the value of tau """ # assign tau and U0 and initialise solution to 0. self.tau.assign(dt) - cpx.set_real(self.U0, x_in) + self.U0.assign(x_in) x_out.assign(0.) # loop over solvers, assigning a_i, solving and accumulating the sum for i in range(self.N): j = self.idx + i - self.ar.assign(self.alpha[j].real) - self.ai.assign(self.alpha[j].imag) + self.ac.real.assign(self.alpha[j].real) + self.ac.imag.assign(self.alpha[j].imag) - self.br.assign(self.beta[j].real) - self.bi.assign(self.beta[j].imag) + self.bc.real.assign(self.beta[j].real) + self.bc.imag.assign(self.beta[j].imag) self.solver.solve() + # accumulate real part of beta*w cpx.get_real(self.w, self.wrk) - x_out += self.br*self.wrk + x_out += self.bc.real*self.wrk cpx.get_imag(self.w, self.wrk) - x_out -= self.bi*self.wrk + x_out -= self.bc.imag*self.wrk # in parallel we have to accumulate the sum over all processes if self.manager is not None: From 33e17fd5b40885b56ce0fcf152270e41ce2b8d89 Mon Sep 17 00:00:00 2001 From: Josh Hope-Collins Date: Tue, 23 Jul 2024 15:26:48 +0100 Subject: [PATCH 41/48] rexi - allow choosing cpx implementation via init arg. --- gusto/rexi/rexi.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/gusto/rexi/rexi.py b/gusto/rexi/rexi.py index aa271e0d6..552cfe8b7 100644 --- a/gusto/rexi/rexi.py +++ b/gusto/rexi/rexi.py @@ -7,7 +7,6 @@ replace_subject, replace_test_function, replace_trial_function ) from firedrake.formmanipulation import split_form -from asQ.complex_proxy import vector as cpx NullTerm = Term(None) @@ -22,7 +21,7 @@ class Rexi(object): """ def __init__(self, equation, rexi_parameters, *, solver_parameters=None, - manager=None): + manager=None, cpx_type='mixed'): """ Args: @@ -33,7 +32,17 @@ def __init__(self, equation, rexi_parameters, *, solver_parameters=None, pass to the solver. Defaults to None. manager (:class:`.Ensemble`): the space and ensemble sub- communicators. Defaults to None. + cpx_type (str, optional): implementation of complex-valued space, + can be 'mixed' or 'vector'. """ + if cpx_type == 'mixed': + from asQ.complex_proxy import mixed as cpx + elif cpx_type == 'vector': + from asQ.complex_proxy import vector as cpx + else: + raise ValueError("cpx_type must be 'mixed' or 'vector'") + self.cpx = cpx + residual = equation.residual.label_map( lambda t: t.has_label(linearisation), map_if_true=lambda t: Term(t.get(linearisation).form, t.labels), @@ -187,7 +196,7 @@ def solve(self, x_out, x_in, dt): :arg dt: the value of tau """ - + cpx = self.cpx # assign tau and U0 and initialise solution to 0. self.tau.assign(dt) self.U0.assign(x_in) From bdb9272981513ca6651e761f1cf48f4981842690 Mon Sep 17 00:00:00 2001 From: Josh Hope-Collins Date: Tue, 23 Jul 2024 15:35:00 +0100 Subject: [PATCH 42/48] rexi - add copy of complex_proxy to gusto. --- gusto/__init__.py | 1 + gusto/complex_proxy/__init__.py | 2 + gusto/complex_proxy/common.py | 143 +++++++++++++++ gusto/complex_proxy/mixed.py | 1 + gusto/complex_proxy/mixed_impl.py | 239 +++++++++++++++++++++++++ gusto/complex_proxy/vector.py | 1 + gusto/complex_proxy/vector_impl.py | 272 +++++++++++++++++++++++++++++ gusto/rexi/rexi.py | 4 +- 8 files changed, 661 insertions(+), 2 deletions(-) create mode 100644 gusto/complex_proxy/__init__.py create mode 100644 gusto/complex_proxy/common.py create mode 100644 gusto/complex_proxy/mixed.py create mode 100644 gusto/complex_proxy/mixed_impl.py create mode 100644 gusto/complex_proxy/vector.py create mode 100644 gusto/complex_proxy/vector_impl.py diff --git a/gusto/__init__.py b/gusto/__init__.py index 168876984..ba545fca0 100644 --- a/gusto/__init__.py +++ b/gusto/__init__.py @@ -28,3 +28,4 @@ from gusto.timeloop import * # noqa from gusto.transport_methods import * # noqa from gusto.wrappers import * # noqa +from gusto import complex_proxy # noqa diff --git a/gusto/complex_proxy/__init__.py b/gusto/complex_proxy/__init__.py new file mode 100644 index 000000000..1b2487e95 --- /dev/null +++ b/gusto/complex_proxy/__init__.py @@ -0,0 +1,2 @@ +import gusto.complex_proxy.mixed # noqa: F401, F403 +import gusto.complex_proxy.vector # noqa: F401, F403 diff --git a/gusto/complex_proxy/common.py b/gusto/complex_proxy/common.py new file mode 100644 index 000000000..98695b764 --- /dev/null +++ b/gusto/complex_proxy/common.py @@ -0,0 +1,143 @@ + +from enum import IntEnum +from collections import namedtuple +from numbers import Number +from firedrake import Constant + +api_names = ["FiniteElement", "FunctionSpace", + "ComplexConstant", "DirichletBC", + "split", "subfunctions", + "get_real", "get_imag", "set_real", "set_imag", + "LinearForm", "BilinearForm", "derivative", + "Part", "re", "im"] + +# flags for real and imaginary parts +Part = IntEnum("Part", (("Real", 0), ("Imag", 1))) +re = Part.Real +im = Part.Imag + +ComplexConstantImpl = namedtuple('ComplexConstant', ['real', 'imag']) + + +def ComplexConstant(z, zi=None): + """ + A namedtuple of 2 firedrake.Constant representing a complex number. + """ + if zi is None: + zr = z.real + zi = z.imag + else: + zr = z + return ComplexConstantImpl(Constant(zr), Constant(zi)) + + +def _flatten_tree(root, is_leaf, get_children, container=tuple): + """ + Return the recursively flattened tree below root in the order that the leafs appear in the traversal. + + :arg root: the current root node. + :arg is_leaf: predicate on root returning True if root has no children. + :arg get_children: unary op on root that returns an iterable of root's children if is_leaf(root) evaluates False. + :arg container: the container type to return the flattened tree in. + """ + if is_leaf(root): + return container((root,)) + else: + return container((leaf + for child in get_children(root) + for leaf in _flatten_tree(child, is_leaf, get_children))) + + +def _complex_components(z): + """ + Return zr and zi for z if z is either complex or ComplexTuple + """ + def is_complex_tuple(z): + return (isinstance(z, tuple) + and len(z) == 2 + and all(isinstance(cmpnt, Constant) for cmpnt in z)) + + if isinstance(z, Number): + zr = Constant(z.real) + zi = Constant(z.imag) + + elif is_complex_tuple(z): + zr = z[0] + zi = z[1] + + else: + msg = "coefficient z should be `complex` or a 2-tuple of `firedrake.Constant`" + raise ValueError(msg) + + return zr, zi + + +def _build_twoform(W, z, A, u, split, return_z): + """ + Return a bilinear Form on the complex FunctionSpace W equal to a complex multiple of a bilinear Form on the real FunctionSpace. + If z = zr + i*zi is a complex number, u = ur + i*ui is a complex (Trial)Function, and b = br + i*bi is a complex linear Form, we want to construct a Form such that (zA)u=b + + (zA)u = (zr*A + i*zi*A)(ur + i*ui) + = (zr*A*ur - zi*A*ui) + i*(zr*A*ui + zi*A*ur) + + = | zr*A -zi*A | | ur | = | br | + | | | | | | + | zi*A zr*A | | ui | = | bi | + + :arg W: the complex-proxy FunctionSpace + :arg z: a complex number or a 2-tuple of firedrake.Constant + :arg A: a generator function for a bilinear Form on the real FunctionSpace, callable as A(*u, *v) where u and v are TrialFunctions and TestFunctions on the real FunctionSpace. + :arg u: a Function or TrialFunction on the complex space + :arg return_z: If true, return Constants for the real/imaginary parts of z used in the BilinearForm. + """ + from firedrake import TestFunction + + v = TestFunction(W) + + ur = split(u, Part.Real) + ui = split(u, Part.Imag) + + vr = split(v, Part.Real) + vi = split(v, Part.Imag) + + zr, zi = _complex_components(z) + + A11 = zr*A(*ur, *vr) + A12 = -zi*A(*ui, *vr) + A21 = zi*A(*ur, *vi) + A22 = zr*A(*ui, *vi) + Ac = A11 + A12 + A21 + A22 + + if return_z: + return Ac, zr, zi + else: + return Ac + + +def _build_oneform(W, z, f, split, return_z): + """ + Return a Linear Form on the complex FunctionSpace W equal to a complex multiple of a linear Form on the real FunctionSpace. + If z = zr + i*zi is a complex number, v = vr + i*vi is a complex TestFunction, we want to construct the Form: + + i + + :arg W: the complex-proxy FunctionSpace. + :arg z: a complex number or a 2-tuple of firedrake.Constant + :arg f: a generator function for a linear Form on the real FunctionSpace, callable as f(*v) where v are TestFunctions on the real FunctionSpace. + :arg return_z: If true, return Constants for the real/imaginary parts of z used in the LinearForm. + """ + from firedrake import TestFunction + + v = TestFunction(W) + vr = split(v, Part.Real) + vi = split(v, Part.Imag) + + zr, zi = _complex_components(z) + + fr = zr*f(*vr) + fi = zi*f(*vi) + fc = fr + fi + + if return_z: + return fc, zr, zi + else: + return fc diff --git a/gusto/complex_proxy/mixed.py b/gusto/complex_proxy/mixed.py new file mode 100644 index 000000000..afa069851 --- /dev/null +++ b/gusto/complex_proxy/mixed.py @@ -0,0 +1 @@ +from gusto.complex_proxy.mixed_impl import * # noqa: F401, F403 diff --git a/gusto/complex_proxy/mixed_impl.py b/gusto/complex_proxy/mixed_impl.py new file mode 100644 index 000000000..a848dc959 --- /dev/null +++ b/gusto/complex_proxy/mixed_impl.py @@ -0,0 +1,239 @@ + +import firedrake as fd + +from gusto.complex_proxy.common import (Part, re, im, api_names, # noqa:F401 + ComplexConstant, # noqa: F401 + _flatten_tree, + _build_oneform, _build_twoform) +__all__ = api_names + + +def FiniteElement(elem): + """ + Return a UFL FiniteElement which proxies a complex version of the real-valued UFL FiniteElement elem. + + The returned complex-valued element has twice as many components as the real-valued element, with + each component of the real-valued element having a corresponding 'real' and 'imaginary' part eg: + Non-mixed real elements become 2-component MixedElements. + Mixed real elements become MixedElements with 2*len(elem.num_sub_elements()) components. + Nested MixedElements are flattened before being proxied. + + :arg elem: the UFL FiniteElement to be proxied + """ + flat_elems = _flatten_tree(elem, + is_leaf=lambda e: type(e) is not fd.MixedElement, + get_children=lambda e: e.sub_elements) + + return fd.MixedElement([e for ee in zip(flat_elems, flat_elems) for e in ee]) + + +def compatible_ufl_elements(elemc, elemr): + """ + Return whether the ufl element elemc is a complex proxy for real ufl element elemr + + :arg elemc: complex proxy ufl element + :arg elemr: real ufl element + """ + return elemc == FiniteElement(elemr) + + +def FunctionSpace(V): + """ + Return a FunctionSpace which proxies a complex version of the real FunctionSpace V. + + The returned complex-valued FunctionSpace has twice as many components as the real-valued element, with + each component of the real-valued FunctionSpace having a corresponding 'real' and 'imaginary' part eg: + Non-mixed real FunctionSpaces become 2-component MixedFunctionSpaces. + Mixed real FunctionSpaces become MixedFunctionSpaces with 2*len(V.ufl_element().num_sub_elements()) components. + Function spaces with nested MixedElements are flattened before being proxied. + + :arg V: the real-valued FunctionSpace. + """ + return fd.FunctionSpace(V.mesh(), FiniteElement(V.ufl_element())) + + +def DirichletBC(W, V, bc, function_arg=None): + """ + Return a DirichletBC on the complex FunctionSpace W that is equivalent to the DirichletBC bc on the real FunctionSpace V that W was constructed from. + + :arg W: the complex FunctionSpace. + :arg V: the real FunctionSpace that W was constructed from. + :arg bc: a DirichletBC on the real FunctionSpace that W was constructed from. + """ + if type(V.ufl_element()) is fd.MixedElement: + off = 2*bc.function_space().index + else: + off = 0 + + if function_arg is None: + function_arg = bc.function_arg + + return tuple((fd.DirichletBC(W.sub(off+i), function_arg, bc.sub_domain) + for i in range(2))) + + +def _component_elements(us, i): + """ + Return a tuple of the real or imaginary components of the iterable us + + :arg us: an iterable having the same number of elements as the complex FunctionSpace + i.e. twice the number of components as the real FunctionSpace. + :arg i: the index of the components, Part.Real for real or Part.Imag for imaginary. + """ + if not isinstance(i, Part): + raise TypeError("i must be a Part enum") + return tuple(us[i::2]) + + +def split(u, i): + """ + If u is a Coefficient or Argument in the complex FunctionSpace, + returns a tuple with the Function components corresponding + to the real or imaginary subelements, indexed appropriately. + Analogous to firedrake.split(u) + + :arg u: a Coefficient or Argument in the complex FunctionSpace + :arg i: the index of the components, Part.Real for real or Part.Imag for imaginary. + """ + return _component_elements(fd.split(u), i) + + +def subfunctions(u, i): + """ + Return a tuple of the real or imaginary components of the complex Function u. Analogous to u.subfunctions. + + :arg u: a complex Function. + :arg i: the index of the components, Part.Real for real or Part.Imag for imaginary. + """ + usub = u if type(u) is tuple else u.subfunctions + return _component_elements(usub, i) + + +def _get_part(u, vout, i): + """ + Copy the real or imaginary part of the complex Function u into the real-valued Function vout. + + :arg u: a complex Function. + :arg vout: a real-valued Function. + :arg i: the index of the components, Part.Real for real or Part.Imag for imaginary. + """ + usub = subfunctions(u, i) + vsub = vout if type(vout) is tuple else vout.subfunctions + + for csub, rsub in zip(usub, vsub): + rsub.assign(csub) + + return vout + + +def _set_part(u, vnew, i): + """ + Set the real or imaginary part of the complex Function u to the value of the real Function vnew. + + :arg u: a complex Function. + :arg vnew: a real Function. + :arg i: the index of the components, Part.Real for real or Part.Imag for imaginary. + """ + usub = subfunctions(u, i) + vsub = vnew if type(vnew) is tuple else vnew.subfunctions + + for csub, rsub in zip(usub, vsub): + csub.assign(rsub) + + +def get_real(u, vout): + """ + Copy the real component of the complex Function u into the real-valued Function vout + + :arg u: a complex Function. + :arg vout: A real-valued Function that real component of u is copied into. + """ + return _get_part(u, vout, Part.Real) + + +def get_imag(u, vout, name=None): + """ + Copy the imaginary component of the complex Function u into the real-valued Function vout + + :arg u: a complex Function. + :arg vout: A real-valued Function that imaginary component of u is copied into. + """ + return _get_part(u, vout, Part.Imag) + + +def set_real(u, vnew): + """ + Copy the real-valued Function vnew into the real part of the complex Function u. + + :arg u: a complex Function. + :arg vnew: A real-value Function. + """ + _set_part(u, vnew, Part.Real) + + +def set_imag(u, vnew): + """ + Copy the real-valued Function vnew into the imaginary part of the complex Function u. + + :arg u: a complex Function. + :arg vnew: A real-value Function. + """ + _set_part(u, vnew, Part.Imag) + + +def LinearForm(W, z, f, return_z=False): + """ + Return a Linear Form on the complex FunctionSpace W equal to a complex multiple of a linear Form on the real FunctionSpace. + If z = zr + i*zi is a complex number, v = vr + i*vi is a complex TestFunction, we want to construct the Form: + + i + + :arg W: the complex-proxy FunctionSpace. + :arg z: a complex number. + :arg f: a generator function for a linear Form on the real FunctionSpace, callable as f(*v) where v are TestFunctions on the real FunctionSpace. + :arg return_z: If true, return Constants for the real/imaginary parts of z used in the LinearForm. + """ + return _build_oneform(W, z, f, split, return_z) + + +def BilinearForm(W, z, A, return_z=False): + """ + Return a bilinear Form on the complex FunctionSpace W equal to a complex multiple of a bilinear Form on the real FunctionSpace. + If z = zr + i*zi is a complex number, u = ur + i*ui is a complex TrialFunction, and b = br + i*bi is a complex linear Form, we want to construct a Form such that (zA)u=b + + (zA)u = (zr*A + i*zi*A)(ur + i*ui) + = (zr*A*ur - zi*A*ui) + i*(zr*A*ui + zi*A*ur) + + = | zr*A -zi*A | | ur | = | br | + | | | | | | + | zi*A zr*A | | ui | = | bi | + + :arg W: the complex-proxy FunctionSpace + :arg z: a complex number. + :arg A: a generator function for a bilinear Form on the real FunctionSpace, callable as A(*u, *v) where u and v are TrialFunctions and TestFunctions on the real FunctionSpace. + :arg return_z: If true, return Constants for the real/imaginary parts of z used in the BilinearForm. + """ + return _build_twoform(W, z, A, fd.TrialFunction(W), split, return_z) + + +def derivative(z, F, u, return_z=False): + """ + Return a bilinear Form equivalent to z*J where z is a complex number, J = dF/dw, F is a nonlinear Form on the real-valued space, and w is a Function in the real-valued space. The real and imaginary components of the complex Function u most both be equal to w for this operation to be valid. + + If z = zr + i*zi is a complex number, x = xr + i*xi is a complex Function, b = br + i*bi is a complex linear Form, J is the bilinear Form dF/dw, we want to construct a Form such that (zJ)x=b + + (zJ)x = (zr*J + i*zi*J)(xr + i*xi) + = (zr*J*xr - zi*J*xi) + i*(zr*A*xi + zi*A*xr) + + = | zr*J -zi*J | | xr | = | br | + | | | | | | + | zi*J zr*J | | xi | = | bi | + + :arg z: a complex number. + :arg F: a generator function for a nonlinear Form on the real FunctionSpace, callable as F(*u, *v) where u and v are Functions and TestFunctions on the real FunctionSpace. + :arg u: the Function to differentiate F with respect to + :arg return_z: If true, return Constants for the real/imaginary parts of z used in the BilinearForm. + """ + def A(*args): + return fd.derivative(F(*args), u) + + return _build_twoform(u.function_space(), z, A, u, split, return_z) diff --git a/gusto/complex_proxy/vector.py b/gusto/complex_proxy/vector.py new file mode 100644 index 000000000..c7fd62375 --- /dev/null +++ b/gusto/complex_proxy/vector.py @@ -0,0 +1 @@ +from gusto.complex_proxy.vector_impl import * # noqa: F401, F403 diff --git a/gusto/complex_proxy/vector_impl.py b/gusto/complex_proxy/vector_impl.py new file mode 100644 index 000000000..10be19b7f --- /dev/null +++ b/gusto/complex_proxy/vector_impl.py @@ -0,0 +1,272 @@ + +import firedrake as fd + +from ufl.classes import MultiIndex, FixedIndex, Indexed + +from gusto.complex_proxy.common import (Part, re, im, api_names, # noqa:F401 + ComplexConstant, # noqa: F401 + _build_oneform, _build_twoform) + +__all__ = api_names + + +def FiniteElement(elem): + """ + Return a UFL FiniteElement which proxies a complex version of the real UFL FiniteElement elem. + + The returned complex-valued element has as many components as the real-valued element, but each component has a 'real' and 'imaginary' part eg: + Scalar real elements become 2-vector complex elements. + n-vector real elements become 2xn-tensor complex elements + (shape)-tensor real elements become (2,shape)-tensor complex elements + + :arg elem: the UFL FiniteElement to be proxied + """ + if isinstance(elem, fd.TensorElement): + shape = (2,) + elem._shape + scalar_element = elem.sub_elements[0] + return fd.TensorElement(scalar_element, shape=shape) + + elif isinstance(elem, fd.VectorElement): + shape = (2, elem.num_sub_elements) + scalar_element = elem.sub_elements[0] + return fd.TensorElement(scalar_element, shape=shape) + + elif isinstance(elem, fd.MixedElement): # recurse + return fd.MixedElement([FiniteElement(e) for e in elem.sub_elements]) + + else: + return fd.VectorElement(elem, dim=2) + + +def compatible_ufl_elements(elemc, elemr): + """ + Return whether the ufl element elemc is a complex proxy for real ufl element elemr + + :arg elemc: complex proxy ufl element + :arg elemr: real ufl element + """ + return elemc == FiniteElement(elemr) + + +def FunctionSpace(V): + """ + Return a FunctionSpace which proxies a complex version of the real FunctionSpace V. + + The returned complex-valued Function space has as many components as the real-valued FunctionSpace, but each component has a 'real' and 'imaginary' part eg: + Scalar components of the real-valued FunctionSpace become 2-vector components of the complex-valued space. + n-vector components of the real-valued FunctionSpace become 2xn-tensor components of the complex-valued space. + (shape)-tensor components of the real-valued FunctionSpace become (2,shape)-tensor components of the complex-valued FunctionSpace. + + :arg V: the real-valued FunctionSpace. + """ + return fd.FunctionSpace(V.mesh(), FiniteElement(V.ufl_element())) + + +def DirichletBC(W, V, bc, function_arg=None): + """ + Return a DirichletBC on the complex FunctionSpace W that is equivalent to the DirichletBC bc on the real FunctionSpace V that W was constructed from. + + :arg W: the complex FunctionSpace. + :arg V: the real FunctionSpace that W was constructed from. + :arg bc: a DirichletBC on the real FunctionSpace that W was constructed from. + """ + if function_arg is None: + function_arg = bc.function_arg + + sub_domain = bc.sub_domain + + if type(V.ufl_element()) is fd.MixedElement: + idx = bc.function_space().index + Ws = (W.sub(idx).sub(0), W.sub(idx).sub(1)) + else: + Ws = (W.sub(0), W.sub(1)) + + return tuple(fd.DirichletBC(Wsub, function_arg, sub_domain) for Wsub in Ws) + + +def split(u, i): + """ + If u is a Coefficient or Argument in the complex FunctionSpace, + returns a tuple with the Function components corresponding + to the real or imaginary subelements, indexed appropriately. + + :arg u: a Coefficient or Argument in the complex FunctionSpace + :arg i: Part.Real for real subelements, Part.Imag for imaginary elements + """ + if not isinstance(i, Part): + raise ValueError("i must be a Part enum") + + us = fd.split(u) + + ncomponents = len(u.function_space().subfunctions) + + if ncomponents == 1: + return tuple((us[i],)) + + def get_sub_element(cpt, i): + part = us[cpt] + idxs = fd.indices(len(part.ufl_shape) - 1) + return fd.as_tensor(Indexed(part, MultiIndex((FixedIndex(i), *idxs))), idxs) + + return tuple(get_sub_element(cpt, i) for cpt in range(ncomponents)) + + +def subfunctions(u, i): + """ + Return a tuple of the real or imaginary components of the complex Function u. Analogous to u.subfunctions. + + :arg u: a complex Function. + :arg i: the index of the components, Part.Real for real or Part.Imag for imaginary. + """ + if type(u) is tuple: + return tuple(v.sub(i) for v in u) + + elem = u.ufl_element() + if isinstance(elem, fd.TensorElement): + num_sub_real = elem.num_sub_elements()//2 + return tuple((u.sub(i*num_sub_real + j) for j in range(num_sub_real))) + + elif isinstance(elem, fd.VectorElement): + return tuple((u.sub(i),)) + + elif isinstance(elem, fd.MixedElement): + return tuple((w for v in u.subfunctions for w in subfunctions(v, i))) + + else: + raise ValueError("u must be a Function from a complex-proxy FunctionSpace") + + +def _get_part(u, vout, i): + """ + Get the real or imaginary part of the complex Function u and copy it to real Function vout. + + :arg u: a complex Function. + :arg vout: a real Function. + :arg i: the index of the components, Part.Real for real or Part.Imag for imaginary. + """ + usub = subfunctions(u, i) + vsub = vout if type(vout) is tuple else vout.subfunctions + + for csub, rsub in zip(usub, vsub): + rsub.assign(csub) + + return vout + + +def _set_part(u, vnew, i): + """ + Set the real or imaginary part of the complex Function u to the value of the real Function vnew. + + :arg u: a complex Function. + :arg vnew: a real Function. + :arg i: the index of the components, Part.Real for real or Part.Imag for imaginary. + """ + usub = subfunctions(u, i) + vsub = vnew if type(vnew) is tuple else vnew.subfunctions + + for csub, rsub in zip(usub, vsub): + csub.assign(rsub) + + +def get_real(u, vout, name=None): + """ + Return a real Function equal to the real component of the complex Function u. + + :arg u: a complex Function. + :arg vout: If a real Function then real component of u is placed here. NotImplementedError(If None then a new Function is returned.) + :arg name: If vout is None, the name of the new Function. Ignored if vout is not none. + """ + if vout is None: + raise NotImplementedError("Inferring real FunctionSpace from complex FunctionSpace not implemented yet") + return _get_part(u, vout, Part.Real) + + +def get_imag(u, vout, name=None): + """ + Return a real Function equal to the imaginary component of the complex Function u. + + :arg u: a complex Function. + :arg vout: If a real Function then the imaginary component of u is placed here. NotImplementedError(If None then a new Function is returned.) + :arg name: If vout is None, the name of the new Function. Ignored if uout is not none. + """ + if vout is None: + raise NotImplementedError("Inferring real FunctionSpace from complex FunctionSpace not implemented yet") + return _get_part(u, vout, Part.Imag) + + +def set_real(u, vnew): + """ + Set the real component of the complex Function u to the value of the real Function vnew. + + :arg u: a complex Function. + :arg vnew: A real Function. + """ + _set_part(u, vnew, Part.Real) + + +def set_imag(u, vnew): + """ + Set the imaginary component of the complex Function u to the value of the real Function vnew. + + :arg u: a complex Function. + :arg vnew: A real Function. + """ + _set_part(u, vnew, Part.Imag) + + +def LinearForm(W, z, f, return_z=False): + """ + Return a Linear Form on the complex FunctionSpace W equal to a complex multiple of a linear Form on the real FunctionSpace. + If z = zr + i*zi is a complex number, v = vr + i*vi is a complex TestFunction, we want to construct the Form: + + i + + :arg W: the complex-proxy FunctionSpace. + :arg z: a complex number. + :arg f: a generator function for a linear Form on the real FunctionSpace, callable as f(*v) where v are TestFunctions on the real FunctionSpace. + :arg return_z: If true, return Constants for the real/imaginary parts of z used in the LinearForm. + """ + return _build_oneform(W, z, f, split, return_z) + + +def BilinearForm(W, z, A, return_z=False): + """ + Return a bilinear Form on the complex FunctionSpace W equal to a complex multiple of a bilinear Form on the real FunctionSpace. + If z = zr + i*zi is a complex number, u = ur + i*ui is a complex TrialFunction, and b = br + i*bi is a complex linear Form, we want to construct a Form such that (zA)u=b + + (zA)u = (zr*A + i*zi*A)(ur + i*ui) + = (zr*A*ur - zi*A*ui) + i*(zr*A*ui + zi*A*ur) + + = | zr*A -zi*A | | ur | = | br | + | | | | | | + | zi*A zr*A | | ui | = | bi | + + :arg W: the complex-proxy FunctionSpace + :arg z: a complex number. + :arg A: a generator function for a bilinear Form on the real FunctionSpace, callable as A(*u, *v) where u and v are TrialFunctions and TestFunctions on the real FunctionSpace. + :arg return_z: If true, return Constants for the real/imaginary parts of z used in the BilinearForm. + """ + return _build_twoform(W, z, A, fd.TrialFunction(W), split, return_z) + + +def derivative(z, F, u, return_z=False): + """ + Return a bilinear Form equivalent to z*J where z is a complex number, J = dF/dw, F is a nonlinear Form on the real-valued space, and w is a Function in the real-valued space. The real and imaginary components of the complex Function u most both be equal to w for this operation to be valid. + + If z = zr + i*zi is a complex number, x = xr + i*xi is a complex Function, b = br + i*bi is a complex linear Form, J is the bilinear Form dF/dw, we want to construct a Form such that (zJ)x=b + + (zJ)x = (zr*J + i*zi*J)(xr + i*xi) + = (zr*J*xr - zi*J*xi) + i*(zr*A*xi + zi*A*xr) + + = | zr*J -zi*J | | xr | = | br | + | | | | | | + | zi*J zr*J | | xi | = | bi | + + :arg z: a complex number. + :arg F: a generator function for a nonlinear Form on the real FunctionSpace, callable as F(*u, *v) where u and v are Functions and TestFunctions on the real FunctionSpace. + :arg u: the Function to differentiate F with respect to + :arg return_z: If true, return Constants for the real/imaginary parts of z used in the BilinearForm. + """ + def A(*args): + return fd.derivative(F(*args), u) + + return _build_twoform(u.function_space(), z, A, u, split, return_z) diff --git a/gusto/rexi/rexi.py b/gusto/rexi/rexi.py index 552cfe8b7..e0abd8cc6 100644 --- a/gusto/rexi/rexi.py +++ b/gusto/rexi/rexi.py @@ -36,9 +36,9 @@ def __init__(self, equation, rexi_parameters, *, solver_parameters=None, can be 'mixed' or 'vector'. """ if cpx_type == 'mixed': - from asQ.complex_proxy import mixed as cpx + from gusto.complex_proxy import mixed as cpx elif cpx_type == 'vector': - from asQ.complex_proxy import vector as cpx + from gusto.complex_proxy import vector as cpx else: raise ValueError("cpx_type must be 'mixed' or 'vector'") self.cpx = cpx From 355dbe6ae335d838dbbb413346e50c0f6d92d101 Mon Sep 17 00:00:00 2001 From: Josh Hope-Collins Date: Tue, 23 Jul 2024 15:39:39 +0100 Subject: [PATCH 43/48] add complex_proxy tests --- .../complex_proxy/test_complex_mixed.py | 681 ++++++++++++++++++ .../complex_proxy/test_complex_vector.py | 681 ++++++++++++++++++ 2 files changed, 1362 insertions(+) create mode 100644 unit-tests/complex_proxy/test_complex_mixed.py create mode 100644 unit-tests/complex_proxy/test_complex_vector.py diff --git a/unit-tests/complex_proxy/test_complex_mixed.py b/unit-tests/complex_proxy/test_complex_mixed.py new file mode 100644 index 000000000..df926fb60 --- /dev/null +++ b/unit-tests/complex_proxy/test_complex_mixed.py @@ -0,0 +1,681 @@ + +import firedrake as fd +import gusto.complex_proxy.mixed as cpx + +import pytest + + +def assemble(form): + return fd.assemble(form).riesz_representation(riesz_map='l2') + + +cell = fd.Cell('triangle') + +scalar_elements = [ + pytest.param(fd.FiniteElement("CG", cell, 1), id="CG1"), + pytest.param(fd.FiniteElement("BDM", cell, 2), id="BDM1") +] + +vector_elements = [ + pytest.param(fd.VectorElement("DG", cell, 1), id="VectorDG1"), + pytest.param(fd.VectorElement("DG", cell, 1, dim=3), id="VectorDG1_3D") +] + +tensor_elements = [ + pytest.param(fd.TensorElement("Lagrange", cell, 1), id="TensorL1"), + pytest.param(fd.TensorElement("Lagrange", cell, 1, shape=(2, 3, 4)), id="TensorL1_234D") +] + +elements = scalar_elements + vector_elements + tensor_elements + + +complex_numbers = [2+0j, 0+3j, 3+2j] + +constant_z = [ + pytest.param(False, id="complex_z"), + pytest.param(True, id="Constant_z"), +] + + +@pytest.fixture +def nx(): + return 10 + + +@pytest.fixture +def mesh(nx): + return fd.UnitSquareMesh(nx, nx) + + +@pytest.fixture +def mixed_element(): + return fd.MixedElement([param.values[0] for param in elements]) + + +@pytest.mark.parametrize("elem", elements) +def test_finite_element(elem): + """ + Test that the complex proxy FiniteElement is constructed correctly from a real FiniteElement. + """ + celem = cpx.FiniteElement(elem) + + assert celem.num_sub_elements == 2, "The cpx element should have two components" + + for ce in celem.sub_elements: + assert ce == elem, "Each component element should be the same as the real element" + + +def test_mixed_element(mixed_element): + """ + Test that the complex proxy FiniteElement is constructed correctly from a real MixedElement. + """ + + celem = cpx.FiniteElement(mixed_element) + + assert celem.num_sub_elements == 2*mixed_element.num_sub_elements, "The cpx element should have twice as many components as the real space" + + csubs = celem.sub_elements + msubs = mixed_element.sub_elements + + for i in range(mixed_element.num_sub_elements): + assert csubs[2*i+0] == msubs[i], "The element for each component should be the same as the real element" + assert csubs[2*i+1] == msubs[i], "The element for each component should be the same as the real element" + + +def test_nested_mixed_element(): + """ + Test that the complex proxy FiniteElement is constructed correctly from a nested real MixedElement. + """ + cg = fd.FiniteElement("CG", cell, 1) + dg = fd.FiniteElement("DG", cell, 2) + + mixed_elem = fd.MixedElement((cg, dg)) + nested_elem = fd.MixedElement((mixed_elem, mixed_elem)) + flat_elem = fd.MixedElement((cg, cg, dg, dg, cg, cg, dg, dg)) + assert cpx.FiniteElement(nested_elem) == flat_elem, "The complex element for mixed spaces should flatten the real space" + + bdm = fd.FiniteElement("BDM", cell, 1) + nested_elem = fd.MixedElement((mixed_elem, bdm)) + flat_elem = fd.MixedElement((cg, cg, dg, dg, bdm, bdm)) + assert cpx.FiniteElement(nested_elem) == flat_elem, "The complex element for mixed spaces should flatten the real space" + + +@pytest.mark.parametrize("elem", elements) +def test_function_space(mesh, elem): + """ + Test that the proxy complex FunctionSpace is correctly constructed for a scalar real FunctionSpace + """ + V = fd.FunctionSpace(mesh, elem) + W = cpx.FunctionSpace(V) + + assert W.ufl_element() == cpx.FiniteElement(elem), "The complex function space should have the cpx Element corresponding to the cpx Element of the real space" + + +def test_mixed_function_space(mesh, mixed_element): + """ + Test that the proxy complex FunctionSpace is correctly constructed for a mixed real FunctionSpace + """ + + V = fd.FunctionSpace(mesh, mixed_element) + W = cpx.FunctionSpace(V) + + assert len(W.subfunctions) == 2*len(V.subfunctions), "The complex space should have twice the number of components as the real space" + + for i in range(V.ufl_element().num_sub_elements): + idx_real = 2*i+0 + idx_imag = 2*i+1 + + real_elem = W.subfunctions[idx_real].ufl_element() + imag_elem = W.subfunctions[idx_imag].ufl_element() + orig_elem = V.subfunctions[i].ufl_element() + + assert real_elem == orig_elem, "The complex function space should have the cpx Element corresponding to the cpx Element of the real space" + assert imag_elem == orig_elem, "The complex function space should have the cpx Element corresponding to the cpx Element of the real space" + + +@pytest.mark.parametrize("split_tuple", [False, True]) +@pytest.mark.parametrize("elem", scalar_elements+vector_elements) +def test_set_get_part(mesh, elem, split_tuple): + """ + Test that the real and imaginary parts are set and get correctly from/to real FunctionSpace + + TODO: add tests for tensor_elements + """ + eps = 1e-12 + + x, y = fd.SpatialCoordinate(mesh) + + if elem.reference_value_shape != (): + dim = elem.reference_value_shape[0] + expr0 = fd.as_vector([x*i for i in range(dim)]) + expr1 = fd.as_vector([-y*i for i in range(dim)]) + else: + expr0 = x + expr1 = -2*y + + V = fd.FunctionSpace(mesh, elem) + W = cpx.FunctionSpace(V) + + u0 = fd.Function(V).project(expr0) + u1 = fd.Function(V).project(expr1) + ur = fd.Function(V).assign(1) + ui = fd.Function(V).assign(1) + w = fd.Function(W).assign(0) + + # test with no tuples, both tuples, and mixed + u0s = u0.subfunctions if split_tuple else u0 + u1s = u1 + urs = ur.subfunctions if split_tuple else ur + uis = ui + ws = w.subfunctions if split_tuple else w + + # check 0 initialisation + cpx.get_real(ws, urs) + cpx.get_imag(ws, uis) + + assert fd.norm(ur) < eps + assert fd.norm(ui) < eps + + # check real value is set correctly and imag value is unchanged + cpx.set_real(ws, u0s) + + cpx.get_real(ws, urs) + cpx.get_imag(ws, uis) + + assert fd.errornorm(u0, ur) < eps, "real component should match after `set_real` called" + assert fd.norm(ui) < eps, "imag component should be unchanged by `set_real`" + + # check imag value is set correctly and real value is unchanged + cpx.set_imag(ws, u1s) + + cpx.get_real(ws, urs) + cpx.get_imag(ws, uis) + + assert fd.errornorm(u0, ur) < eps, "real component should be unchanged by `set_real`" + assert fd.errornorm(u1, ui) < eps, "imag component should match after `set_real` called" + + +def test_mixed_set_get_part(mesh): + """ + Test that the real and imaginary parts are set and get correctly from/to real FunctionSpace + + TODO: add tests for tensor_elements + """ + eps = 1e-12 + + x, y = fd.SpatialCoordinate(mesh) + + # set up mixed real space and initialise some functions + + V0 = fd.FunctionSpace(mesh, "BDM", 1) + V1 = fd.FunctionSpace(mesh, "DG", 0) + V = V0*V1 + + u0 = fd.Function(V) + u1 = fd.Function(V) + ur = fd.Function(V).assign(1) + ui = fd.Function(V).assign(1) + + u0.sub(0).project(fd.as_vector([x, 2*x*x])) + u1.sub(0).project(fd.as_vector([-3*y, -4*y*y])) + + u0.sub(1).project(5*x) + u1.sub(1).project(-6*y) + + # set up complex mixed space + + W = cpx.FunctionSpace(V) + + w = fd.Function(W).assign(0) + cpx.get_real(w, ur) + cpx.get_imag(w, ui) + + assert fd.norm(ur) < eps + assert fd.norm(ui) < eps + + # check real value is set correctly and imag value is unchanged + cpx.set_real(w, u0) + + cpx.get_real(w, ur) + cpx.get_imag(w, ui) + + assert fd.errornorm(u0, ur) < eps, "real component should match after `set_real` called" + assert fd.norm(ui) < eps, "imag component should be unchanged by `set_real`" + + # check imag value is set correctly and real value is unchanged + cpx.set_imag(w, u1) + + cpx.get_real(w, ur) + cpx.get_imag(w, ui) + + assert fd.errornorm(u0, ur) < eps, "real component should be unchanged by `set_real`" + assert fd.errornorm(u1, ui) < eps, "imag component should match after `set_real` called" + + +@pytest.mark.parametrize("elem", scalar_elements+vector_elements) +@pytest.mark.parametrize("z", complex_numbers) +@pytest.mark.parametrize("z_is_constant", constant_z) +def test_linear_form(mesh, elem, z, z_is_constant): + """ + Test that the linear Form is constructed correctly + + TODO: add tests for tensor_elements + """ + eps = 1e-12 + + if z_is_constant: + z = cpx.ComplexConstant(z) + + V = fd.FunctionSpace(mesh, elem) + W = cpx.FunctionSpace(V) + + x, y = fd.SpatialCoordinate(mesh) + + if elem.reference_value_shape != (): + vec_expr = [x*x-y, y+x, -y-0.5*x] + dim = elem.reference_value_shape[0] + f = fd.as_vector(vec_expr[:dim]) + else: + f = x*x-y + + def L(v): + return fd.inner(f, v)*fd.dx + + v = fd.TestFunction(V) + rhs = assemble(L(v)) + + ur = fd.Function(V) + ui = fd.Function(V) + w = fd.Function(W) + + w = assemble(cpx.LinearForm(W, z, L)) + + cpx.get_real(w, ur) + cpx.get_imag(w, ui) + + zr = z.real + zi = z.imag + assert fd.errornorm(zr*rhs, ur) < eps, "z*LinearForm just does componentwise multiplication" + assert fd.errornorm(zi*rhs, ui) < eps, "z*LinearForm just does componentwise multiplication" + + +@pytest.mark.parametrize("elem", scalar_elements+vector_elements) +@pytest.mark.parametrize("z_is_constant", constant_z) +def test_bilinear_form(mesh, elem, z_is_constant): + """ + Test that the bilinear form is constructed correctly + + TODO: add tests for tensor_elements + """ + eps = 1e-12 + + # set up the real problem + V = fd.FunctionSpace(mesh, elem) + + x, y = fd.SpatialCoordinate(mesh) + + if elem.reference_value_shape != (): + vec_expr = [x*x-y, y+x, -y-0.5*x] + dim = elem.reference_value_shape[0] + expr = fd.as_vector(vec_expr[:dim]) + else: + expr = x*x-y + + f = fd.Function(V).interpolate(expr) + + def form_function(u, v): + return fd.inner(u, v)*fd.dx + + u = fd.TrialFunction(V) + v = fd.TestFunction(V) + + a = form_function(u, v) + + # the real value + b = assemble(fd.action(a, f)) + + # set up the complex problem + W = cpx.FunctionSpace(V) + + g = fd.Function(W) + + cpx.set_real(g, f) + f.assign(2*f) + cpx.set_imag(g, f) + + # real/imag parts of mat-vec product + br = fd.Function(V) + bi = fd.Function(V) + + # non-zero only on diagonal blocks: real and imag parts independent + zr = 3+0j + if z_is_constant: + zr = cpx.ComplexConstant(zr) + + K = cpx.BilinearForm(W, zr, form_function) + wr = assemble(fd.action(K, g)) + + cpx.get_real(wr, br) + cpx.get_imag(wr, bi) + + assert fd.errornorm(3*1*b, br) < eps, "If z is purely real, just does component-wise multiplication" + assert fd.errornorm(3*2*b, bi) < eps, "If z is purely real, just does component-wise multiplication" + + # non-zero only on off-diagonal blocks: real and imag parts independent + zi = 0+4j + if z_is_constant: + zi = cpx.ComplexConstant(zi) + + K = cpx.BilinearForm(W, zi, form_function) + wi = assemble(fd.action(K, g)) + + cpx.get_real(wi, br) + cpx.get_imag(wi, bi) + + assert fd.errornorm(-4*2*b, br) < eps, "if z is purely imag, does off-diagonal multiplication" + assert fd.errornorm(4*1*b, bi) < eps, "if z is purely imag, does off-diagonal multiplication" + + # non-zero in all blocks: + if not z_is_constant: + z = zr + zi + else: + z = cpx.ComplexConstant(zr.real, zi.imag) + + K = cpx.BilinearForm(W, z, form_function) + wz = assemble(fd.action(K, g)) + + cpx.get_real(wz, br) + cpx.get_imag(wz, bi) + + # mat-vec multiplication should be linear + br_check = fd.Function(V) + bi_check = fd.Function(V) + + wz.assign(wr + wi) + + cpx.get_real(wz, br_check) + cpx.get_imag(wz, bi_check) + + assert fd.errornorm(br_check, br) < eps, "MatVecMult z*A*x should be linear in z" + assert fd.errornorm(bi_check, bi) < eps, "MatVecMult z*A*x should be linear in z" + + +@pytest.mark.parametrize("bc_type", ["nobc", "dirichletbc"]) +def test_linear_solve(mesh, bc_type): + """ + Test that the bilinear form is constructed correctly + + TODO: add tests for tensor_elements + """ + from math import pi + + eps = 1e-12 + + # set up the real problem + V = fd.FunctionSpace(mesh, "CG", 1) + + x, y = fd.SpatialCoordinate(mesh) + + def form_function(u, v): + return (fd.inner(u, v) + fd.dot(fd.grad(u), fd.grad(v)))*fd.dx + + f = (2*pi**2)*fd.sin(pi*x)*fd.sin(pi*y) + + def rhs(v): + return fd.inner(f, v)*fd.dx + + # the real solution + u = fd.TrialFunction(V) + v = fd.TestFunction(V) + A = form_function(u, v) + L = rhs(v) + + if bc_type == "nobc": + bcs = [] + elif bc_type == "dirichletbc": + bcs = [fd.DirichletBC(V, 0, 1)] + else: + raise ValueError(f"Unrecognised boundary condition type: {bc_type}") + + ureal = fd.Function(V) + fd.solve(A == L, ureal, bcs=bcs) + + # set up the complex problem + W = cpx.FunctionSpace(V) + + cpx_bcs = [] + for bc in bcs: + cpx_bcs.extend([*cpx.DirichletBC(W, V, bc)]) + + # A non-zero only on diagonal blocks: real and imag parts independent + zr = 3+0j + zl = 1+2j + + A = cpx.BilinearForm(W, zr, form_function) + L = cpx.LinearForm(W, zl, rhs) + + wr = fd.Function(W) + + fd.solve(A == L, wr, bcs=cpx_bcs) + + wcheckr = fd.Function(V) + wchecki = fd.Function(V) + + cpx.get_real(wr, wcheckr) + cpx.get_imag(wr, wchecki) + + assert fd.errornorm(1*ureal/3, wcheckr) < eps, "If z is purely real, equivalent to component-wise solve" + assert fd.errornorm(2*ureal/3, wchecki) < eps, "If z is purely real, equivalent to component-wise solve" + + # non-zero on all blocks but imag part of rhs is zero + zi = 2+4j + + A = cpx.BilinearForm(W, zi, form_function) + L = cpx.LinearForm(W, 1, rhs) + + wi = fd.Function(W) + + fd.solve(A == L, wi, bcs=cpx_bcs) + + cpx.get_real(wi, wcheckr) + cpx.get_imag(wi, wchecki) + + # eliminate imaginary part to check real part + assert fd.errornorm(2*ureal/(2*2+4*4), wcheckr) < eps, "real part should be calculated from eliminating the imaginary part" + + # back substitute real part to check imaginary part + g = 0.25*(2*fd.action(form_function(u, v), wcheckr) - rhs(v)) + a = form_function(u, v) + + ui = fd.Function(V) + fd.solve(a == g, ui, bcs=bcs) + + assert fd.errornorm(ui, wchecki) < eps, "imag part should be calculated from back-substituting the real part" + + +@pytest.mark.parametrize("bc_type", ["nobc", "dirichletbc"]) +def test_mixed_linear_solve(mesh, bc_type): + """ + Test that the bilinear form is constructed correctly + + TODO: add tests for tensor_elements + """ + eps = 1e-12 + + # set up the real problem + V0 = fd.FunctionSpace(mesh, "BDM", 1) + V1 = fd.FunctionSpace(mesh, "DG", 0) + V = V0*V1 + + x, y = fd.SpatialCoordinate(mesh) + + def form_function(sigma, u, tau, v): + return (fd.dot(sigma, tau) + fd.div(tau)*u + fd.div(sigma)*v)*fd.dx + + def rhs(tau, v): + f = 10*fd.exp(-(pow(x - 0.5, 2) + pow(y - 0.5, 2)) / 0.02) + return -fd.inner(f, v)*fd.dx + + if bc_type == "nobc": + bcs = [] + elif bc_type == "dirichletbc": + bcs = [ + fd.DirichletBC(V.sub(1), 0, 3), + fd.DirichletBC(V.sub(1), 0, 4) + ] + else: + raise ValueError(f"Unrecognised boundary condition type: {bc_type}") + + # the real solution + u = fd.TrialFunctions(V) + v = fd.TestFunctions(V) + A = form_function(*u, *v) + L = rhs(*v) + + ureal = fd.Function(V) + fd.solve(A == L, ureal, bcs=bcs) + + # set up the complex problem + W = cpx.FunctionSpace(V) + + cpx_bcs = [] + for bc in bcs: + cpx_bcs.extend([*cpx.DirichletBC(W, V, bc)]) + + # A non-zero only on diagonal blocks: real and imag parts independent + zr = 3+0j + zl = 1+2j + + A = cpx.BilinearForm(W, zr, form_function) + L = cpx.LinearForm(W, zl, rhs) + + wr = fd.Function(W) + + fd.solve(A == L, wr, bcs=cpx_bcs) + + wcheckr = fd.Function(V) + wchecki = fd.Function(V) + + cpx.get_real(wr, wcheckr) + cpx.get_imag(wr, wchecki) + + assert fd.errornorm(1*ureal/3, wcheckr) < eps, "If z is purely real, equivalent to component-wise solve" + assert fd.errornorm(2*ureal/3, wchecki) < eps, "If z is purely real, equivalent to component-wise solve" + + # non-zero on all blocks but imag part of rhs is zero + zi = 2+4j + + A = cpx.BilinearForm(W, zi, form_function) + L = cpx.LinearForm(W, 1, rhs) + + wi = fd.Function(W) + + fd.solve(A == L, wi, bcs=cpx_bcs) + + cpx.get_real(wi, wcheckr) + cpx.get_imag(wi, wchecki) + + # eliminate imaginary part to check real part + assert fd.errornorm(2*ureal/(2*2+4*4), wcheckr) < eps, "real part should be calculated from eliminating the imaginary part" + + # back substitute real part to check imaginary part + g = 0.25*(2*fd.action(form_function(*u, *v), wcheckr) - rhs(*v)) + a = form_function(*u, *v) + + ui = fd.Function(V) + fd.solve(a == g, ui, bcs=bcs) + + assert fd.errornorm(ui, wchecki) < eps, "imag part should be calculated from back-substituting the real part" + + +def test_derivative_solve(mesh): + """ + Test that the bilinear form is constructed correctly + + TODO: add tests for tensor_elements + """ + from math import pi + + eps = 1e-12 + + # set up the real problem + V = fd.FunctionSpace(mesh, "CG", 1) + + x, y = fd.SpatialCoordinate(mesh) + + mu = fd.Constant(0.05) + + def form_function(u, v): + return (fd.inner(u, v) + (1 + mu*fd.inner(u, u))*fd.dot(fd.grad(u), fd.grad(v)))*fd.dx + + f = (1 + 8*pi*pi)*fd.cos(2*pi*x)*fd.cos(2*pi*y) + + def rhs(v): + return fd.inner(f, v)*fd.dx + + init_expr = fd.cos(2*pi*x)*fd.cos(2*pi*y) + + # the real solution + u = fd.Function(V).project(init_expr) + v = fd.TestFunction(V) + F = form_function(u, v) + A = fd.derivative(F, u) + L = rhs(v) + + ureal = fd.Function(V) + fd.solve(A == L, ureal) + + # set up the complex problem + W = cpx.FunctionSpace(V) + + # linearise around "u" + u0 = fd.Function(W) + cpx.set_real(u0, u) + cpx.set_imag(u0, u) + + # A non-zero only on diagonal blocks: real and imag parts independent + zr = 3+0j + zl = 1+2j + + A = cpx.derivative(zr, form_function, u0) + L = cpx.LinearForm(W, zl, rhs) + + wr = fd.Function(W) + + fd.solve(A == L, wr) + + wcheckr = fd.Function(V) + wchecki = fd.Function(V) + + cpx.get_real(wr, wcheckr) + cpx.get_imag(wr, wchecki) + + assert fd.errornorm(1*ureal/3, wcheckr) < eps, "If z is purely real, equivalent to component-wise solve" + assert fd.errornorm(2*ureal/3, wchecki) < eps, "If z is purely real, equivalent to component-wise solve" + + # non-zero on all blocks but imag part of rhs is zero + zi = 2+4j + + A = cpx.derivative(zi, form_function, u0) + L = cpx.LinearForm(W, 1, rhs) + + wi = fd.Function(W) + + fd.solve(A == L, wi) + + cpx.get_real(wi, wcheckr) + cpx.get_imag(wi, wchecki) + + # eliminate imaginary part to check real part + assert fd.errornorm(2*ureal/(2*2+4*4), wcheckr) < eps, "real part should be calculated from eliminating the imaginary part" + + # back substitute real part to check imaginary part + ut = fd.Function(V).project(init_expr) + F = form_function(ut, v) + A = fd.derivative(F, ut) + g = 0.25*(2*fd.action(A, wcheckr) - rhs(v)) + + ui = fd.Function(V) + fd.solve(A == g, ui) + + assert fd.errornorm(ui, wchecki) < eps, "imag part should be calculated from back-substituting the real part" diff --git a/unit-tests/complex_proxy/test_complex_vector.py b/unit-tests/complex_proxy/test_complex_vector.py new file mode 100644 index 000000000..b73525ad7 --- /dev/null +++ b/unit-tests/complex_proxy/test_complex_vector.py @@ -0,0 +1,681 @@ + +import firedrake as fd +import gusto.complex_proxy.vector as cpx + +import pytest + + +def assemble(form): + return fd.assemble(form).riesz_representation(riesz_map='l2') + + +cell = fd.Cell('triangle') + +scalar_elements = [ + pytest.param(fd.FiniteElement("CG", cell, 1), id="CG1"), + pytest.param(fd.FiniteElement("BDM", cell, 2), id="BDM1") +] + +vector_elements = [ + pytest.param(fd.VectorElement("DG", cell, 1), id="VectorDG1"), + pytest.param(fd.VectorElement("DG", cell, 1, dim=3), id="VectorDG1_3D") +] + +tensor_elements = [ + pytest.param(fd.TensorElement("Lagrange", cell, 1), id="TensorL1"), + pytest.param(fd.TensorElement("Lagrange", cell, 1, shape=(2, 3, 4)), id="TensorL1_234D") +] + +elements = scalar_elements + vector_elements + tensor_elements + + +complex_numbers = [2+0j, 0+3j, 3+2j] + +constant_z = [ + pytest.param(False, id="complex_z"), + pytest.param(True, id="Constant_z"), +] + + +@pytest.fixture +def nx(): + return 10 + + +@pytest.fixture +def mesh(nx): + return fd.UnitSquareMesh(nx, nx) + + +@pytest.fixture +def mixed_element(): + return fd.MixedElement([param.values[0] for param in elements]) + + +@pytest.mark.parametrize("elem", scalar_elements) +def test_finite_element(elem): + """ + Test that the complex proxy FiniteElement is constructed correctly from a real FiniteElement. + """ + celem = cpx.FiniteElement(elem) + + assert celem.num_sub_elements == 2, "The cpx element should have two subcomponents" + + for ce in celem.sub_elements: + assert ce == elem, "Each component element should be the same as the real element" + + +@pytest.mark.parametrize("elem", vector_elements) +def test_vector_element(elem): + """ + Test that the complex proxy FiniteElement is constructed correctly from a real VectorElement. + """ + celem = cpx.FiniteElement(elem) + + assert celem.num_sub_elements == 2*elem.num_sub_elements, "The cpx element of a vector element should have twice as many subcomponents as the real space" + + assert celem._shape == (2, elem.num_sub_elements), "The cpx element of a vector element should be a tensor element" + + for ce in celem.sub_elements: + assert ce == elem.sub_elements[0], "The element for each component should be the same as the real element" + + +@pytest.mark.parametrize("elem", tensor_elements) +def test_tensor_element(elem): + """ + Test that the complex proxy FiniteElement is constructed correctly from a real TensorElement. + """ + celem = cpx.FiniteElement(elem) + + assert celem.num_sub_elements == 2*elem.num_sub_elements, "The cpx element of a tensor should have twice as many subcomponents as the real space" + + assert celem._shape == (2,) + elem._shape, "The cpx element of a tensor element should be a tensor element of one order higher" + + for ce in celem.sub_elements: + assert ce == elem.sub_elements[0], "The element for each component should be the same as the real element" + + +def test_mixed_element(mixed_element): + """ + Test that the complex proxy FiniteElement is constructed correctly from a real MixedElement. + """ + + celem = cpx.FiniteElement(mixed_element) + + assert celem.num_sub_elements == mixed_element.num_sub_elements, "The cpx element of a mixed element should have the same number of components as the real element" + + for csub, msub in zip(celem.sub_elements, mixed_element.sub_elements): + assert csub == cpx.FiniteElement(msub), "The each component of the cpx element of a mixed real space should be the cpx element of the real component" + + +@pytest.mark.parametrize("elem", elements) +def test_function_space(mesh, elem): + """ + Test that the proxy complex FunctionSpace is correctly constructed for a scalar real FunctionSpace + """ + V = fd.FunctionSpace(mesh, elem) + W = cpx.FunctionSpace(V) + + assert W.ufl_element() == cpx.FiniteElement(elem), "The complex function space should have the cpx Element corresponding to the cpx Element of the real space" + + +def test_mixed_function_space(mesh, mixed_element): + """ + Test that the proxy complex FunctionSpace is correctly constructed for a mixed real FunctionSpace + """ + + V = fd.FunctionSpace(mesh, mixed_element) + W = cpx.FunctionSpace(V) + + assert len(W.subfunctions) == len(V.subfunctions), "The complex space should have the same number of components as the real space" + + for wcpt, vcpt in zip(W.subfunctions, V.subfunctions): + assert wcpt == cpx.FunctionSpace(vcpt), "Each component of the complex space should be the complex space of the component of the real space" + + +@pytest.mark.parametrize("split_tuple", [False, True]) +@pytest.mark.parametrize("elem", scalar_elements) +def test_set_get_part(mesh, elem, split_tuple): + """ + Test that the real and imaginary parts are set and get correctly from/to real FunctionSpace + + TODO: add tests for vector_elements and tensor_elements + """ + eps = 1e-12 + + x, y = fd.SpatialCoordinate(mesh) + + if elem.reference_value_shape != (): + dim = elem.reference_value_shape[0] + expr0 = fd.as_vector([x*i for i in range(dim)]) + expr1 = fd.as_vector([-y*i for i in range(dim)]) + else: + expr0 = x + expr1 = -2*y + + V = fd.FunctionSpace(mesh, elem) + W = cpx.FunctionSpace(V) + + u0 = fd.Function(V).project(expr0) + u1 = fd.Function(V).project(expr1) + ur = fd.Function(V).assign(1) + ui = fd.Function(V).assign(1) + w = fd.Function(W).assign(0) + + # test with no tuples, both tuples, and mixed + u0s = u0.subfunctions if split_tuple else u0 + u1s = u1 + urs = ur.subfunctions if split_tuple else ur + uis = ui + ws = w.subfunctions if split_tuple else w + + # check 0 initialisation + cpx.get_real(ws, urs) + cpx.get_imag(ws, uis) + + assert fd.norm(ur) < eps + assert fd.norm(ui) < eps + + # check real value is set correctly and imag value is unchanged + cpx.set_real(ws, u0s) + + cpx.get_real(ws, urs) + cpx.get_imag(ws, uis) + + assert fd.errornorm(u0, ur) < eps, "real component should match after `set_real` called" + assert fd.norm(ui) < eps, "imag component should be unchanged by `set_real`" + + # check imag value is set correctly and real value is unchanged + cpx.set_imag(ws, u1s) + + cpx.get_real(ws, urs) + cpx.get_imag(ws, uis) + + assert fd.errornorm(u0, ur) < eps, "real component should be unchanged by `set_real`" + assert fd.errornorm(u1, ui) < eps, "imag component should match after `set_real` called" + + +def test_mixed_set_get_part(mesh): + """ + Test that the real and imaginary parts are set and get correctly from/to real FunctionSpace + + TODO: add tests for tensor_elements + """ + eps = 1e-12 + + x, y = fd.SpatialCoordinate(mesh) + + # set up mixed real space and initialise some functions + + V0 = fd.FunctionSpace(mesh, "BDM", 1) + V1 = fd.FunctionSpace(mesh, "DG", 0) + V = V0*V1 + + u0 = fd.Function(V) + u1 = fd.Function(V) + ur = fd.Function(V).assign(1) + ui = fd.Function(V).assign(1) + + u0.sub(0).project(fd.as_vector([x, 2*x*x])) + u1.sub(0).project(fd.as_vector([-3*y, -4*y*y])) + + u0.sub(1).project(5*x) + u1.sub(1).project(-6*y) + + # set up complex mixed space + + W = cpx.FunctionSpace(V) + + w = fd.Function(W).assign(0) + cpx.get_real(w, ur) + cpx.get_imag(w, ui) + + assert fd.norm(ur) < eps + assert fd.norm(ui) < eps + + # check real value is set correctly and imag value is unchanged + cpx.set_real(w, u0) + + cpx.get_real(w, ur) + cpx.get_imag(w, ui) + + assert fd.errornorm(u0, ur) < eps, "real component should match after `set_real` called" + assert fd.norm(ui) < eps, "imag component should be unchanged by `set_real`" + + # check imag value is set correctly and real value is unchanged + cpx.set_imag(w, u1) + + cpx.get_real(w, ur), "real component should be unchanged by `set_real`" + cpx.get_imag(w, ui), "imag component should match after `set_real` called" + + assert fd.errornorm(u0, ur) < eps + assert fd.errornorm(u1, ui) < eps + + +@pytest.mark.parametrize("elem", scalar_elements[:1]) +@pytest.mark.parametrize("z", complex_numbers) +@pytest.mark.parametrize("z_is_constant", constant_z) +def test_linear_form(mesh, elem, z, z_is_constant): + """ + Test that the linear Form is constructed correctly + + TODO: add tests for tensor_elements + """ + eps = 1e-12 + + if z_is_constant: + z = cpx.ComplexConstant(z) + + V = fd.FunctionSpace(mesh, elem) + W = cpx.FunctionSpace(V) + + x, y = fd.SpatialCoordinate(mesh) + + if elem.reference_value_shape != (): + vec_expr = [x*x-y, y+x, -y-0.5*x] + dim = elem.reference_value_shape[0] + f = fd.as_vector(vec_expr[:dim]) + else: + f = x*x-y + + def L(v): + return fd.inner(f, v)*fd.dx + + v = fd.TestFunction(V) + rhs = assemble(L(v)) + + ur = fd.Function(V) + ui = fd.Function(V) + w = fd.Function(W) + + w = assemble(cpx.LinearForm(W, z, L)) + + cpx.get_real(w, ur) + cpx.get_imag(w, ui) + + zr = z.real + zi = z.imag + assert fd.errornorm(zr*rhs, ur) < eps, "z*LinearForm just does componentwise multiplication" + assert fd.errornorm(zi*rhs, ui) < eps, "z*LinearForm just does componentwise multiplication" + + +@pytest.mark.parametrize("elem", scalar_elements[:1]) +@pytest.mark.parametrize("z_is_constant", constant_z) +def test_bilinear_form(mesh, elem, z_is_constant): + """ + Test that the bilinear form is constructed correctly + + TODO: add tests for tensor_elements + """ + eps = 1e-12 + + # set up the real problem + V = fd.FunctionSpace(mesh, elem) + + x, y = fd.SpatialCoordinate(mesh) + + if elem.reference_value_shape != (): + vec_expr = [x*x-y, y+x, -y-0.5*x] + dim = elem.reference_value_shape[0] + expr = fd.as_vector(vec_expr[:dim]) + else: + expr = x*x-y + + f = fd.Function(V).interpolate(expr) + + def form_function(u, v): + return fd.inner(u, v)*fd.dx + + u = fd.TrialFunction(V) + v = fd.TestFunction(V) + + a = form_function(u, v) + + # the real value + b = assemble(fd.action(a, f)) + + # set up the complex problem + W = cpx.FunctionSpace(V) + + g = fd.Function(W) + + cpx.set_real(g, f) + f.assign(2*f) + cpx.set_imag(g, f) + + # real/imag parts of mat-vec product + br = fd.Function(V) + bi = fd.Function(V) + + # non-zero only on diagonal blocks: real and imag parts independent + zr = 3+0j + if z_is_constant: + zr = cpx.ComplexConstant(zr) + + K = cpx.BilinearForm(W, zr, form_function) + wr = assemble(fd.action(K, g)) + + cpx.get_real(wr, br) + cpx.get_imag(wr, bi) + + assert fd.errornorm(3*1*b, br) < eps, "If z is purely real, just does component-wise multiplication" + assert fd.errornorm(3*2*b, bi) < eps, "If z is purely real, just does component-wise multiplication" + + # non-zero only on off-diagonal blocks: real and imag parts independent + zi = 0+4j + if z_is_constant: + zi = cpx.ComplexConstant(zi) + + K = cpx.BilinearForm(W, zi, form_function) + wi = assemble(fd.action(K, g)) + + cpx.get_real(wi, br) + cpx.get_imag(wi, bi) + + assert fd.errornorm(-4*2*b, br) < eps, "if z is purely imag, does off-diagonal multiplication" + assert fd.errornorm(4*1*b, bi) < eps, "if z is purely imag, does off-diagonal multiplication" + + # non-zero in all blocks: + if not z_is_constant: + z = zr + zi + else: + z = cpx.ComplexConstant(zr.real, zi.imag) + + K = cpx.BilinearForm(W, z, form_function) + wz = assemble(fd.action(K, g)) + + cpx.get_real(wz, br) + cpx.get_imag(wz, bi) + + # mat-vec multiplication should be linear + br_check = fd.Function(V) + bi_check = fd.Function(V) + + wz.assign(wr + wi) + + cpx.get_real(wz, br_check) + cpx.get_imag(wz, bi_check) + + assert fd.errornorm(br_check, br) < eps, "MatVecMult z*A*x should be linear in z" + assert fd.errornorm(bi_check, bi) < eps, "MatVecMult z*A*x should be linear in z" + + +@pytest.mark.parametrize("bc_type", ["nobc", "dirichletbc"]) +def test_linear_solve(mesh, bc_type): + """ + Test that the bilinear form is constructed correctly + + TODO: add tests for tensor_elements + """ + from math import pi + + eps = 1e-12 + + # set up the real problem + V = fd.FunctionSpace(mesh, "CG", 1) + + x, y = fd.SpatialCoordinate(mesh) + + def form_function(u, v): + return (fd.inner(u, v) + fd.dot(fd.grad(u), fd.grad(v)))*fd.dx + + f = (2*pi**2)*fd.sin(pi*x)*fd.sin(pi*y) + + def rhs(v): + return fd.inner(f, v)*fd.dx + + # the real solution + u = fd.TrialFunction(V) + v = fd.TestFunction(V) + A = form_function(u, v) + L = rhs(v) + + if bc_type == "nobc": + bcs = [] + elif bc_type == "dirichletbc": + bcs = [fd.DirichletBC(V, 0, 1)] + else: + raise ValueError(f"Unrecognised boundary condition type: {bc_type}") + + ureal = fd.Function(V) + fd.solve(A == L, ureal, bcs=bcs) + + # set up the complex problem + W = cpx.FunctionSpace(V) + + cpx_bcs = [] + for bc in bcs: + cpx_bcs.extend([*cpx.DirichletBC(W, V, bc)]) + + # A non-zero only on diagonal blocks: real and imag parts independent + zr = 3+0j + zl = 1+2j + + A = cpx.BilinearForm(W, zr, form_function) + L = cpx.LinearForm(W, zl, rhs) + + wr = fd.Function(W) + + fd.solve(A == L, wr, bcs=cpx_bcs) + + wcheckr = fd.Function(V) + wchecki = fd.Function(V) + + cpx.get_real(wr, wcheckr) + cpx.get_imag(wr, wchecki) + + assert fd.errornorm(1*ureal/3, wcheckr) < eps, "If z is purely real, equivalent to component-wise solve" + assert fd.errornorm(2*ureal/3, wchecki) < eps, "If z is purely real, equivalent to component-wise solve" + + # non-zero on all blocks but imag part of rhs is zero + zi = 2+4j + + A = cpx.BilinearForm(W, zi, form_function) + L = cpx.LinearForm(W, 1, rhs) + + wi = fd.Function(W) + + fd.solve(A == L, wi, bcs=cpx_bcs) + + cpx.get_real(wi, wcheckr) + cpx.get_imag(wi, wchecki) + + # eliminate imaginary part to check real part + assert fd.errornorm(2*ureal/(2*2+4*4), wcheckr) < eps, "real part should be calculated from eliminating the imaginary part" + + # back substitute real part to check imaginary part + g = 0.25*(2*fd.action(form_function(u, v), wcheckr) - rhs(v)) + a = form_function(u, v) + + ui = fd.Function(V) + fd.solve(a == g, ui, bcs=bcs) + + assert fd.errornorm(ui, wchecki) < eps, "real part should be calculated from eliminating the imaginary part" + + +@pytest.mark.parametrize("bc_type", ["nobc", "dirichletbc"]) +def test_mixed_linear_solve(mesh, bc_type): + """ + Test that the bilinear form is constructed correctly + + TODO: add tests for tensor_elements + """ + eps = 1e-12 + + # set up the real problem + V0 = fd.FunctionSpace(mesh, "BDM", 1) + V1 = fd.FunctionSpace(mesh, "DG", 0) + V = V0*V1 + + x, y = fd.SpatialCoordinate(mesh) + + def form_function(sigma, u, tau, v): + return (fd.dot(sigma, tau) + fd.div(tau)*u + fd.div(sigma)*v)*fd.dx + + def rhs(tau, v): + f = 10*fd.exp(-(pow(x - 0.5, 2) + pow(y - 0.5, 2)) / 0.02) + return -fd.inner(f, v)*fd.dx + + if bc_type == "nobc": + bcs = [] + elif bc_type == "dirichletbc": + bcs = [ + fd.DirichletBC(V.sub(1), 0, 3), + fd.DirichletBC(V.sub(1), 0, 4) + ] + else: + raise ValueError(f"Unrecognised boundary condition type: {bc_type}") + + # the real solution + u = fd.TrialFunctions(V) + v = fd.TestFunctions(V) + A = form_function(*u, *v) + L = rhs(*v) + + ureal = fd.Function(V) + fd.solve(A == L, ureal, bcs=bcs) + + # set up the complex problem + W = cpx.FunctionSpace(V) + + cpx_bcs = [] + for bc in bcs: + cpx_bcs.extend([*cpx.DirichletBC(W, V, bc)]) + + # A non-zero only on diagonal blocks: real and imag parts independent + zr = 3+0j + zl = 1+2j + + A = cpx.BilinearForm(W, zr, form_function) + L = cpx.LinearForm(W, zl, rhs) + + wr = fd.Function(W) + + fd.solve(A == L, wr, bcs=cpx_bcs) + + wcheckr = fd.Function(V) + wchecki = fd.Function(V) + + cpx.get_real(wr, wcheckr) + cpx.get_imag(wr, wchecki) + + assert fd.errornorm(1*ureal/3, wcheckr) < eps, "If z is purely real, equivalent to component-wise solve" + assert fd.errornorm(2*ureal/3, wchecki) < eps, "If z is purely real, equivalent to component-wise solve" + + # non-zero on all blocks but imag part of rhs is zero + zi = 2+4j + + A = cpx.BilinearForm(W, zi, form_function) + L = cpx.LinearForm(W, 1, rhs) + + wi = fd.Function(W) + + fd.solve(A == L, wi, bcs=cpx_bcs) + + cpx.get_real(wi, wcheckr) + cpx.get_imag(wi, wchecki) + + # eliminate imaginary part to check real part + assert fd.errornorm(2*ureal/(2*2+4*4), wcheckr) < eps + + # back substitute real part to check imaginary part + g = 0.25*(2*fd.action(form_function(*u, *v), wcheckr) - rhs(*v)) + a = form_function(*u, *v) + + ui = fd.Function(V) + fd.solve(a == g, ui, bcs=bcs) + + assert fd.errornorm(ui, wchecki) < eps, "imag part should be calculated from back-substituting the real part" + + +def test_derivative_solve(mesh): + """ + Test that the bilinear form is constructed correctly + + TODO: add tests for tensor_elements + """ + from math import pi + + eps = 1e-12 + + # set up the real problem + V = fd.FunctionSpace(mesh, "CG", 1) + + x, y = fd.SpatialCoordinate(mesh) + + mu = fd.Constant(0.05) + + def form_function(u, v): + return (fd.inner(u, v) + (1 + mu*fd.inner(u, u))*fd.dot(fd.grad(u), fd.grad(v)))*fd.dx + + f = (1 + 8*pi*pi)*fd.cos(2*pi*x)*fd.cos(2*pi*y) + + def rhs(v): + return fd.inner(f, v)*fd.dx + + init_expr = fd.cos(2*pi*x)*fd.cos(2*pi*y) + + # the real solution + u = fd.Function(V).project(init_expr) + v = fd.TestFunction(V) + F = form_function(u, v) + A = fd.derivative(F, u) + L = rhs(v) + + ureal = fd.Function(V) + fd.solve(A == L, ureal) + + # set up the complex problem + W = cpx.FunctionSpace(V) + + # linearise around "u" + u0 = fd.Function(W) + cpx.set_real(u0, u) + cpx.set_imag(u0, u) + + # A non-zero only on diagonal blocks: real and imag parts independent + zr = 3+0j + zl = 1+2j + + A = cpx.derivative(zr, form_function, u0) + L = cpx.LinearForm(W, zl, rhs) + + wr = fd.Function(W) + + fd.solve(A == L, wr) + + wcheckr = fd.Function(V) + wchecki = fd.Function(V) + + cpx.get_real(wr, wcheckr) + cpx.get_imag(wr, wchecki) + + assert fd.errornorm(1*ureal/3, wcheckr) < eps, "If z is purely real, equivalent to component-wise solve" + assert fd.errornorm(2*ureal/3, wchecki) < eps, "If z is purely real, equivalent to component-wise solve" + + # non-zero on all blocks but imag part of rhs is zero + zi = 2+4j + + A = cpx.derivative(zi, form_function, u0) + L = cpx.LinearForm(W, 1, rhs) + + wi = fd.Function(W) + + fd.solve(A == L, wi) + + cpx.get_real(wi, wcheckr) + cpx.get_imag(wi, wchecki) + + # eliminate imaginary part to check real part + assert fd.errornorm(2*ureal/(2*2+4*4), wcheckr) < eps, "real part should be calculated from eliminating the imaginary part" + + # back substitute real part to check imaginary part + ut = fd.Function(V).project(init_expr) + F = form_function(ut, v) + A = fd.derivative(F, ut) + g = 0.25*(2*fd.action(A, wcheckr) - rhs(v)) + + ui = fd.Function(V) + fd.solve(A == g, ui) + + assert fd.errornorm(ui, wchecki) < eps, "imag part should be calculated from back-substituting the real part" From 1865563a799f7ea104ce9760df820bce7ebf39da Mon Sep 17 00:00:00 2001 From: jshipton Date: Tue, 23 Jul 2024 17:09:36 +0100 Subject: [PATCH 44/48] fix import --- gusto/rexi/rexi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gusto/rexi/rexi.py b/gusto/rexi/rexi.py index e0abd8cc6..2824e7ca7 100644 --- a/gusto/rexi/rexi.py +++ b/gusto/rexi/rexi.py @@ -1,7 +1,7 @@ from gusto.rexi.rexi_coefficients import * from firedrake import Function, DirichletBC, \ LinearVariationalProblem, LinearVariationalSolver -from gusto.labels import time_derivative, prognostic, linearisation +from gusto.core.labels import time_derivative, prognostic, linearisation from firedrake.fml import ( Term, all_terms, drop, subject, replace_subject, replace_test_function, replace_trial_function From 99375999e86f3cf7d824929a094b65965a444ea3 Mon Sep 17 00:00:00 2001 From: Josh Hope-Collins Date: Wed, 24 Jul 2024 17:12:23 +0100 Subject: [PATCH 45/48] rexi - implement form_rhs using form_mass. --- gusto/rexi/rexi.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/gusto/rexi/rexi.py b/gusto/rexi/rexi.py index 2824e7ca7..8aee68f14 100644 --- a/gusto/rexi/rexi.py +++ b/gusto/rexi/rexi.py @@ -138,13 +138,7 @@ def form_function(*trials_and_tests): # generate ufl for right hand side over given trial/tests def form_rhs(*tests): - rhs = mass.label_map( - all_terms, - replace_test_function(tests)) - rhs = rhs.label_map( - all_terms, - replace_subject(self.U0)) - return rhs + return form_mass(*self.U0.subfunctions, *tests) # complex Constants for alpha and beta values self.ac = cpx.ComplexConstant(1) From 1442d07b13fed673a4c6fdf5b357fe6bed89f0b4 Mon Sep 17 00:00:00 2001 From: Jack Betteridge Date: Mon, 16 Dec 2024 16:14:45 +0000 Subject: [PATCH 46/48] was removed --- gusto/core/io.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gusto/core/io.py b/gusto/core/io.py index faf27ad0c..6c569d19d 100644 --- a/gusto/core/io.py +++ b/gusto/core/io.py @@ -29,7 +29,7 @@ class GustoIOError(IOError): ) -def pick_up_mesh(output, mesh_name, comm=COMM_WORLD): +def pick_up_mesh(output, mesh_name): """ Picks up a checkpointed mesh. This must be the first step of any model being picked up from a checkpointing run. @@ -52,7 +52,7 @@ def pick_up_mesh(output, mesh_name, comm=COMM_WORLD): else: dumpdir = path.join("results", output.dirname) chkfile = path.join(dumpdir, "chkpt.h5") - with CheckpointFile(chkfile, 'r', comm=comm) as chk: + with CheckpointFile(chkfile, 'r') as chk: mesh = chk.load_mesh(mesh_name) if dumpdir: From 7f25e76e908dacf4cfd24bc93729f49e8f66616d Mon Sep 17 00:00:00 2001 From: Jack Betteridge Date: Mon, 16 Dec 2024 16:42:57 +0000 Subject: [PATCH 47/48] Revert " was removed" This reverts commit 1442d07b13fed673a4c6fdf5b357fe6bed89f0b4. --- gusto/core/io.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gusto/core/io.py b/gusto/core/io.py index 6c569d19d..faf27ad0c 100644 --- a/gusto/core/io.py +++ b/gusto/core/io.py @@ -29,7 +29,7 @@ class GustoIOError(IOError): ) -def pick_up_mesh(output, mesh_name): +def pick_up_mesh(output, mesh_name, comm=COMM_WORLD): """ Picks up a checkpointed mesh. This must be the first step of any model being picked up from a checkpointing run. @@ -52,7 +52,7 @@ def pick_up_mesh(output, mesh_name): else: dumpdir = path.join("results", output.dirname) chkfile = path.join(dumpdir, "chkpt.h5") - with CheckpointFile(chkfile, 'r') as chk: + with CheckpointFile(chkfile, 'r', comm=comm) as chk: mesh = chk.load_mesh(mesh_name) if dumpdir: From 40065b341b14f360ffb47ff56e3836788ae5ada1 Mon Sep 17 00:00:00 2001 From: Jack Betteridge Date: Mon, 16 Dec 2024 16:47:28 +0000 Subject: [PATCH 48/48] Update docstring to include new arg --- gusto/core/io.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gusto/core/io.py b/gusto/core/io.py index faf27ad0c..710495828 100644 --- a/gusto/core/io.py +++ b/gusto/core/io.py @@ -40,6 +40,8 @@ def pick_up_mesh(output, mesh_name, comm=COMM_WORLD): mesh_name (str): the name of the mesh to be picked up. The default names used by Firedrake are "firedrake_default" for non-extruded meshes, or "firedrake_default_extruded" for extruded meshes. + comm: MPI communicator over which the mesh should be defined after being + "picked-up". Returns: :class:`Mesh`: the mesh to be used by the model.