From 7cc8daf74068ddac500c08b72a8fe730c5ec29a4 Mon Sep 17 00:00:00 2001 From: Hantao Cui Date: Sun, 31 Mar 2024 17:16:54 +0000 Subject: [PATCH 1/4] Update. --- andes/core/symprocessor.py | 11 +++++++++-- docs/source/release-notes.rst | 6 ++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/andes/core/symprocessor.py b/andes/core/symprocessor.py index 06f1202ea..e0aa4a119 100644 --- a/andes/core/symprocessor.py +++ b/andes/core/symprocessor.py @@ -118,11 +118,18 @@ def generate_symbols(self): self.tex_names[name] = sp.Symbol(tex_name) # ----------------------------------------------------------- + # `all_params_names` include parameters, services, exports from blocks, etc. for var in self.cache.all_params_names: + is_real = True + + if var in self.parent.services: + if self.parent.services[var].vtype == complex: + is_real = False + self.inputs_dict[var] = sp.Symbol(var) for var in self.cache.all_vars_names: - tmp = sp.Symbol(var) + tmp = sp.Symbol(var, real=True) # all DAE variables are real self.vars_dict[var] = tmp self.inputs_dict[var] = tmp if var in self.cache.vars_int: @@ -130,7 +137,7 @@ def generate_symbols(self): # store tex names defined in `self.config` for key in self.config.as_dict(): - tmp = sp.Symbol(key) + tmp = sp.Symbol(key, real=True) # not expecting complex numbers in config self.inputs_dict[key] = tmp if key in self.config.tex_names: self.tex_names[tmp] = sp.Symbol(self.config.tex_names[key]) diff --git a/docs/source/release-notes.rst b/docs/source/release-notes.rst index d766892bc..ae131afad 100644 --- a/docs/source/release-notes.rst +++ b/docs/source/release-notes.rst @@ -9,6 +9,12 @@ The APIs before v3.0.0 are in beta and may change without prior notice. v1.9 Notes ========== +v1.9.3 (2024-04-XX) +------------------- +- In symbolic processor, most variables are assumed to be real, except some + services that are specified as complex. This will allow generating simplified + expressions. + v1.9.2 (2024-03-25) ------------------- - Improve PSS/E parser for the `wmod` field in the static generator From 0496b552b6dd015609c58d0f838bbe8148f406be Mon Sep 17 00:00:00 2001 From: Hantao Cui Date: Sun, 31 Mar 2024 18:02:49 +0000 Subject: [PATCH 2/4] Make `need_diag_eps` unique. --- andes/core/symprocessor.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/andes/core/symprocessor.py b/andes/core/symprocessor.py index e0aa4a119..55e154509 100644 --- a/andes/core/symprocessor.py +++ b/andes/core/symprocessor.py @@ -418,6 +418,8 @@ def generate_jacobians(self, diag_eps=1e-8): self.calls.append_ijv(f'{var.e_code}{var.v_code}c', e_idx, v_idx, eps) self.calls.need_diag_eps.append(var.name) + self.calls.need_diag_eps = sorted(list(set(self.calls.need_diag_eps))) + def generate_pretty_print(self): """ Generate pretty print variables and equations. From 6a8790bbc61d3e769ab3f0444e52baee48c11540 Mon Sep 17 00:00:00 2001 From: Hantao Cui Date: Tue, 2 Apr 2024 03:48:29 +0000 Subject: [PATCH 3/4] Add DPLine model. --- andes/models/__init__.py | 2 +- andes/models/line/__init__.py | 1 + andes/models/line/dpline.py | 145 ++++++++++++++++++++++++++++++++++ andes/routines/tds.py | 2 +- andes/system.py | 7 +- 5 files changed, 154 insertions(+), 3 deletions(-) create mode 100644 andes/models/line/dpline.py diff --git a/andes/models/__init__.py b/andes/models/__init__.py index 094e939b9..daf1fecc1 100644 --- a/andes/models/__init__.py +++ b/andes/models/__init__.py @@ -18,7 +18,7 @@ ('static', ['PQ', 'PV', 'Slack']), ('shunt', ['Shunt', "ShuntTD", 'ShuntSw']), ('interface', ['Fortescue']), - ('line', ['Line', 'Jumper']), + ('line', ['Line', 'Jumper', 'DPLine']), ('area', ['Area', 'ACE', 'ACEc']), ('dynload', ['ZIP', 'FLoad']), ('synchronous', ['GENCLS', 'GENROU', 'PLBVFU1']), diff --git a/andes/models/line/__init__.py b/andes/models/line/__init__.py index 7f3931ca5..f8477efc8 100644 --- a/andes/models/line/__init__.py +++ b/andes/models/line/__init__.py @@ -4,3 +4,4 @@ from andes.models.line.line import Line # noqa from andes.models.line.jumper import Jumper # noqa +from andes.models.line.dpline import DPLine # noqa diff --git a/andes/models/line/dpline.py b/andes/models/line/dpline.py new file mode 100644 index 000000000..d99f4b096 --- /dev/null +++ b/andes/models/line/dpline.py @@ -0,0 +1,145 @@ +""" +AC transmission line in dynamic phasor (shift frequency) formulation. +""" + + +from andes.core import (ModelData, IdxParam, + Model, ExtAlgeb, ConstService, ExtParam, State) + + +class DPLineData(ModelData): + """ + Data for Line. + """ + + def __init__(self): + super().__init__() + + self.line = IdxParam(info='Line index', model='Line') + + +class DPLine(DPLineData, Model): + """ + WIP + + AC transmission line model in dynamic phasor (shift frequency) formulation. + + The connectivity `u` logic is as follows: + - `u = 0` means the line is not replaced by DPLine. The line remains in the + phasor model, and the `u` of the phasor Line device applies. + - `u = 1` means the line is replaced by DPLine. Still, the connectivity + status of the DP line depends on the `u` of the phasor Line device. + + """ + + def __init__(self, system=None, config=None): + DPLineData.__init__(self) + Model.__init__(self, system, config) + self.group = 'ACLine' + self.flags.pflow = False + self.flags.tds = True + self.flags.tds_init = True + + self.bus1 = ExtParam(model='Line', src='bus1', indexer=self.line, export=False) + self.bus2 = ExtParam(model='Line', src='bus2', indexer=self.line, export=False) + + self.a1 = ExtAlgeb(model='Bus', src='a', indexer=self.bus1, tex_name='a_1', + info='phase angle of the from bus', + ename='Pij', + tex_ename='P_{ij}', + ) + self.a2 = ExtAlgeb(model='Bus', src='a', indexer=self.bus2, tex_name='a_2', + info='phase angle of the to bus', + ename='Pji', + tex_ename='P_{ji}', + ) + self.v1 = ExtAlgeb(model='Bus', src='v', indexer=self.bus1, tex_name='v_1', + info='voltage magnitude of the from bus', + ename='Qij', + tex_ename='Q_{ij}', + ) + self.v2 = ExtAlgeb(model='Bus', src='v', indexer=self.bus2, tex_name='v_2', + info='voltage magnitude of the to bus', + ename='Qji', + tex_ename='Q_{ji}', + ) + + self.ul = ExtParam(model='Line', src='u', indexer=self.line, tex_name='u', export=False) + self.g1 = ExtParam(model='Line', src='g1', indexer=self.line, tex_name='g_1', export=False) + self.g2 = ExtParam(model='Line', src='g2', indexer=self.line, tex_name='g_2', export=False) + self.b1 = ExtParam(model='Line', src='b1', indexer=self.line, tex_name='b_1', export=False) + self.b2 = ExtParam(model='Line', src='b2', indexer=self.line, tex_name='b_2', export=False) + self.r = ExtParam(model='Line', src='r', indexer=self.line, tex_name='r', export=False) + self.x = ExtParam(model='Line', src='x', indexer=self.line, tex_name='x', export=False) + self.g = ExtParam(model='Line', src='g', indexer=self.line, tex_name='g', export=False) + self.b = ExtParam(model='Line', src='b', indexer=self.line, tex_name='b', export=False) + self.tap = ExtParam(model='Line', src='tap', indexer=self.line, tex_name='tap', export=False) + + self.gh = ConstService(tex_name='g_h') + self.bh = ConstService(tex_name='b_h') + self.gk = ConstService(tex_name='g_k') + self.bk = ConstService(tex_name='b_k') + + self.yh = ConstService(tex_name='y_h', vtype=complex) + self.yk = ConstService(tex_name='y_k', vtype=complex) + self.yhk = ConstService(tex_name='y_{hk}', vtype=complex) + + self.ghk = ConstService(tex_name='g_{hk}') + self.bhk = ConstService(tex_name='b_{hk}') + + self.itap = ConstService(tex_name='1/t_{ap}') + self.itap2 = ConstService(tex_name='1/t_{ap}^2') + + self.Leq = ConstService(v_str='x/(2*pi*60)') + + self.ue = ConstService(v_str='ul * u') + + self.gh.v_str = 'g1 + 0.5 * g' + self.bh.v_str = 'b1 + 0.5 * b' + self.gk.v_str = 'g2 + 0.5 * g' + self.bk.v_str = 'b2 + 0.5 * b' + + self.yh.v_str = 'u * (gh + 1j * bh)' + self.yk.v_str = 'u * (gk + 1j * bk)' + self.yhk.v_str = 'u/((r+1e-8) + 1j*(x+1e-8))' + + self.ghk.v_str = 're(yhk)' + self.bhk.v_str = 'im(yhk)' + + self.itap.v_str = '1/tap' + self.itap2.v_str = '1/tap/tap' + + self.r2x2 = ConstService(v_str='r*r + x*x') + + # in implicit form but has the same dq-axis alignment as in ANDES implementation) + self.idd = State(info='real current', + tex_name='idd', + v_str='ue * (r*v1*sin(a1)/r2x2 - r*v2*sin(a2)/r2x2 - v1*x*cos(a1)/r2x2 +' + ' v2*x*cos(a2)/r2x2)', + e_str='ue * (-x*iqq - r*idd - v2*sin(a2) + v1*sin(a1)) + (1-ue) * idd', + t_const=self.Leq, + ) + + self.iqq = State(info='real current', + tex_name='iqq', + v_str='ue * (r*v1*cos(a1)/r2x2 - r*v2*cos(a2)/r2x2 + v1*x*sin(a1)/r2x2 -' + ' v2*x*sin(a2)/r2x2)', + e_str='ue * (x*idd - r*iqq - v2*cos(a2) + v1*cos(a1)) + (1-ue) * iqq', + t_const=self.Leq, + ) + + self.a1.e_str = 'ue * (idd*v1*sin(a1) + iqq*v1*cos(a1))' + + self.v1.e_str = 'ue * (-idd*v1*cos(a1) + iqq*v1*sin(a1))' + + self.a2.e_str = 'ue * (-idd*v2*sin(a2) - iqq*v2*cos(a2))' + + self.v2.e_str = 'ue * (idd*v2*cos(a2) - iqq*v2*sin(a2))' + + def v_numeric(self, **kwargs): + """ + Disable phasor lines that have been replaced by DP lines. + """ + + mask_idx = [self.line.v[i] for i in range(self.n) if self.u.v[i] == 1] + self.system.groups['ACLine'].set(src='u', idx=mask_idx, attr='v', value=0) diff --git a/andes/routines/tds.py b/andes/routines/tds.py index 816159e9b..378e231c3 100644 --- a/andes/routines/tds.py +++ b/andes/routines/tds.py @@ -813,7 +813,7 @@ def do_switch(self): # check system connectivity after a switching if ret is True and self.config.check_conn == 1: - system.connectivity(info=False) + system.connectivity(info=False, routine='tds') return ret diff --git a/andes/system.py b/andes/system.py index 1536cd87a..5879a4409 100644 --- a/andes/system.py +++ b/andes/system.py @@ -1196,7 +1196,7 @@ def vars_to_models(self): if var.n > 0: var.v[:] = self.dae.x[var.a] - def connectivity(self, info=True): + def connectivity(self, info=True, routine='pflow'): """ Perform connectivity check for system. @@ -1225,6 +1225,11 @@ def connectivity(self, info=True): to.extend(self.Line.a2.a.tolist()) u.extend(self.Line.u.v.tolist()) + if routine == 'tds': + fr.extend(self.DPLine.a1.a.tolist()) + to.extend(self.DPLine.a2.a.tolist()) + u.extend(self.DPLine.u.v.tolist()) + # collect from Jumper fr.extend(self.Jumper.a1.a.tolist()) to.extend(self.Jumper.a2.a.tolist()) From 64f26955bf0b8bcd823a1b1f6c4d3ffb77df7556 Mon Sep 17 00:00:00 2001 From: Hantao Cui Date: Tue, 2 Apr 2024 03:52:38 +0000 Subject: [PATCH 4/4] Add test case. --- andes/cases/smib/SMIB_DPLine.xlsx | Bin 0 -> 14843 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 andes/cases/smib/SMIB_DPLine.xlsx diff --git a/andes/cases/smib/SMIB_DPLine.xlsx b/andes/cases/smib/SMIB_DPLine.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..5e41aaaeeb726581a9a175ac5e9707cf6a8dd413 GIT binary patch literal 14843 zcmaJ|1z21;*T$t-ad&rj*W&JOgS$ILi^~)#?ykk%9ZGR6?%o1L3;eX*-G1BM{qOS- zX6~Hx<|a2slDtur0f%@2^8FI7577Ma;g169`M-e^z{Z(T@edi8A2L>*Z3DZ{ze$6E zfWZAF!|+|Q0@y9x$MiDPfPBoCcLiO{wALHh$e!dY=z!@Gy)7#-cK!X81d_24COTiJ zX~)xjug&!ZTdIb9bF!h4-mD=P*B9S?IzIO6GF&DtY7ZRp>JU`(g|miOK44P3&#?(d z2y#tGo>iOp*y#}Jjx@;|r)lX_$x&uktYL&1X1l{XoN0F`x|G74nfKbCHwl1!QD`P= z7KW)R+iRv$%U3aHeeJS%m$GE%<#>GeYILyab&M-x-8z!c{*X6s*k!psaFwl*hP3iit! zXiqf3rxo86Dp6L%|<~>Ombh^j230!5vTe zrrO{)Ant1{ppxsT%9=~H!sBZb(p`MHe!SoZp3fd38HXZ<$0bne75O#q5&>`cvZWSW zPoqMUc@TjwC_&Bmi1o}R@|_=+)Y>XZP*4-rvBDZDQ4dksMkEoz!ozh3^(d8T^xX9h z!0|^%`N(0%(KNH*(;h`;_&0k6vAK+&#EKo+aK~q4;?KDppE2*{O`#xHU!0VMEz= z_90Ds7L(9dK|PApyJQlq%PZ3bmCEL{qt2X}AzsTfsIv)MB>`ctK<5X)Xey>tcO60C zvT_e6%jC-@3;R55zL{st%z6GJ5cKl|7!4BfyMz45^Et#)*>D= zK@i(L2RRtpwRPcW6aT*2c^xK>+yTwV3UW2o$ZQ>ore^E(MX>|;0x4~717UbcF8Swy z%Z`;k{Bf;Vxao`&D*cUdHdut6#~#VUKp$rRG#i0Ye}K^-h~q`RHw5}4Nkt-W2` z3=2%u!8nI=@IAm4&|~D{gBQU)QOkzdPzJ{c97>3$iH-0Oz(@+ZYXwm|63uZ%&zPtE zbjFUa>`Zd@0|Gu7)hqs`zc9JaJgQ`%oXb6#G3Z5x@9 zqm|Y5kfn1;kx9`CF?at4@gYaFGZz0&jQ+{ox7nt@(sORkcry|D3>IKx@Byot+7&f< z-G&Jwm%yU1pIb0`-8`o<5Ds##Gk*OLwKVqurYO6xl0CTQCBAfm6Ko@#d4Y|uc)@yY zq32z02{`flr43SwYR!=GxBPMh>|@tJ

ua*Srkvb?c#W{{i z#`U9Z93zOduQ1W4nXi-7Dm6kCukx9^=6S-)Qr24W<^`A!OO)7F)QhCK6H_}SkH(PC zBKOeAw!~Cr)89D0P$sX#-QKD@l3mZ<$tGo5w)Plbj8In)<01;_&756ZlJ=*?Q6`?H zNKzUvDB88x13JH3`>cI|)-E-Dn_O<;c8F;n;>}WP%0k?sElmBUA1{lBPk?HHLx<}BtnM8xq@6>*gOsn&h6T>~a1<3w#4M^{zN70PSO zE7_CdubY^Zz1hZALJyJATGAsRh+8RxXCueWF}X2SFH=IASw3W$I7u#0(T=47xI8wgdT`IN4LtNVcm$&W*-Mh>aE*a94Hdi#WDH)KjzH*~Tx6iX(z_;@-U$sza zO3gloyW>F@pjCKJ+5tWMD$s!OI^Ls8c#$d4Xt*KYp`^jN3{opMQkY)AY9ME75RUoiHW@v;Jb4#h}V*V>ikwxT+ru2O!ym26))Vz?J0OkhW#hLdJt>yZXDAb9FCUf=>lhN8DK z*cal!p=vCwH{Mkf5VpJS=k>&q$HK}sg&rHbz!QbxTMGE?exsk>|$R&ta!+RWPMwYT3YLJeZ<<<0@BLqqq+_4Q^fKfr zV&^&(LULLnxYSbHwm`3B>2zl?fLOC1YjNDx{~-n;9LA-!Bq6zPz+50}i#TrIj`Bqf zXhAcbxeFVXG8lkFMZePQaxb>13|G3&2r%+Ot0(GIc)FEqN&0J{Gl*z)#|&#Ku8JJL z0wp(0A#_b3iUvyw1S^*st%Al2&BF+>joaRhdP*!tOSyNY&R8s?9$;`O<&X)5p;T1w zgs*w_5(Os2oDk)izBW`=qEOR_(Rp@%SQVy59=`NclkQJ~EL@EiNy1yOo9czjq$&ZJ zL4{dIU9qltH4*?fhxELa9LeSj*U8(ca_7MiPWBXsPNEP?Fkh`IB{|93q=})KvU&-o z3WTa}Ky-51xLV8Fcqb`$EDk$c8TD1~(4xg*b=|_?tCmA7YX{x3kUxPm1d@l^uZJwk zGZru_`-F@czArys2aN!`zM@|2RQJ%t5Oe!b4Op;SCLnC%Z>}9IrZ%=80k;*eKZpV=qt-ft>n6s9n8;xjrD*d8a8(B0M zz7h_l9>=sjc|E<^PclbYiF59)5NEsP^F2vHj+A@QvypyZT`7$Wm~T~k*2{y1M@@

HB6S=lS5FEOZ_cVVq=uRd~nLmFQ zt!@Lk_D=hY@>{U18;%o*-nrXPSZk1W!RCscB#5AQEFHe}zI=lG z(-wY?Z>EPn!t>|E)8IL2{EzX?^<#W%>Nqd)V11({=!Dk6*yRu^f+a7I(@urk(^_Eh zFxDZE3Z5`97{;`M!zVP0@c>y|v-)aTABkOYGT*~nxmtX zJK`v{gb_2f9NtG)X!(fEF$QdkOB_TLamUw~WL)`NTja3)u$#Hw4YAJZwFTYmF4Vd9 zZ@y6wSu#*HIHFvr2wUk`GHIQ#h)i-Iw?2aSqRo8j?}zloflgpT`I4DkhSUD(VgZ{s z&{BZ@wBo==8j&xYwXG0?H$OLLnqGd$(X6$BHH*hw6DtybRJ4XF#_Ym1kd%kEAx791 zS_1#ab!5D1Qnds|ex zJcq&BL6_!^Q?B2Cn4tv?rD@dv;$wLS=$v2w66aEx!TUQ2?`N9?&ud4doy$?0Q|jko z8R4na9F%Z{w8ICVr#s`;@1nEEXIf9$sUU?KGlQQY2KQH?D0qD8a~*$e|E?(p2Mdd$ zavCbsjJ)y!O2W+Zs1LL@{^D@R(~NX)HvVEvVw_iSI!bJuI;*!o&nJK@Re_rOaa3hF z7gAWt!*6I={R?vf%w!RSZ;}p!bZ$@I%5^iZ>*|gb`euWe2Lglz=_vzu8;7yuFz9j^ zV=`X$S|}ZR^o{LMI~0qBXonS1FNbNt0bnN7XIf~$cE~-NCZY}rCoK0Q4=Ok8J}($P z?WOoi*7J>1ZE&TThk^rfrE96@Prh?PXs!lq&p*z~z^KlwSP=Qox{7%`(h9Gt!Jd}_ zTo$RVAI#uJ4ptIh7Q@Ynu-Wk#Pje_`Kx zegRV-V6aeKRdYq(#ui61i+@~QW!7@;`=x+ek9+I6zH>b%B(?iePW5WrwZnoYEVmXc zx8^1j@Yss*hybI;fYwp#1(xbEm?t>5o5sAWPfFeg#qDnI4D+fIA6;<-&7A6bW5Z52 zqqJaSUXVr>A=6jrL~##C##g)FUQ7Dc4^s81>W9yghYpkj^Joc z@OyaaVelSh`hdTL>9GG6$U$7^JkVw#}#!ac2v zvxK~RN)N9yuiP~i5eqd)>&O;gBJ2o`|Eo`bB^Gdmgx}i*V8kgb1s)r76Fm@k)tG&3 zCp?&t2+2D_6CSe_7Z&6tY)^3kz(AM!tk*M+WQ;OcTXRLtKu`)p8<~U~mIuFI&*85(jFqzkg9_jex2X4sdngFzwzKwh|Gd_Wpq^&^PExY#3D^+ICk|tg? zQ%3Yr`FI4hTcz%afzCEl`*Yw|*S6i<(VeQfr{qsHqjGN&4Ib5baHH~ftZ({Ov~d*B z2>SOo!027nim;F@BPO)m-H^(lp19XHUv!2nEjeiRZ4f?v+WBpzLrMAqGYSF(WCr^` zMywnZ^Ofl;Op-0lYpS@dCjW;pal4mew(imT zDC_CoWO@gYQY?>;=uiWca>`lasHm9ojA3dKqL42K26Si z6Tr9EI*+fU-U%&^oeFQ>~SHt8U73b{Mqy-S*UWwed|D)#-5 z9Rr5ek_%od@`o#6BqTlRRb<3!E5Wtk!KV)mR~e4uJ+v*`h22`|dg&8HFN2Jx7x?>f zc{hW@7!r~bvCVo_t9EqFJZt-xFkBAo!2CWo?9CWd)MjT&j_`QaEo4m1px#rH@HQnE zc+p8-R7vc=y3NkHPxM!~Wmt}jx3qbHG>9~B618dN6oNpit!l9JNK>WTE|`(q$|&?v zmt8H*-(*2QQ@O`cK4F<%ExKaM2eKquWLQ_po>*pBcgmhv%i$-KfLE0CvbYwryQ&t( z)D`Ad_GOoGy2oNprO|?lcEXMpK0XDJn@BR6N-Bqjt?CYAPr=~;^NHJwiHC8f;BbL; z#O+PQ!?;s$cu8GG?OjH&cyn-gtNyu0?ae^W@?=gr&t5OjULfbE={v`8a{Hz#PL1Ro&CsSRGazm&U4(VrtDo`Ac=or5K z1a^bxLfim7NqAP|FzF~ML4U<*+W26ezL7pjc!kCOJyk`k^?NFBm$kafgj0b!&FA7- z<5Zyjd(rq@bbP&0yMnvI!KolUMnP-L^yck~zD(C6<>nXRzAYzV+j2~t5?Nub zA*qe(Wk1Apj6=>9l3X1LJr4epw7QG@zL_42! z!3#KFyTMsF3$=pRc?)@i)V(rn%zWKqF60ebry=D1_RT0zvIGgQ=zRO7?s1UAd)g){ zey#>wXnlTRN{%j>+O{gq@zR}<*sc_W&&EM*3ojWthTw|zmp~ZZqA|ZZBNn%T*69eP zfiK}_uYfOsXY+%3KS0dzgD=5lw}UOERcz)8IN;r%IS@l{h|n}qL0`RNb5t5XfdzH2 zOu9})9*21i2kJmf!~oF~^1A8uA^DFlLOTfAD_~1_6Q>|21QP@xC&&v&l-Z})6Z#-0 z=o7195s`OYj?bmqts0%1#^sWnx?chgsNU|UuD!bi%tnpapdjo?v60jiz_>^X1`z!zs*Koq}jX88AuxKy=vr zGL9}kjca*){d#X_5etmLp`jc0!w@_H?3=9JX>b6e&#N#HC*J{c2THN$jGN$8GQu-< zm+Wm)SPdE+H$dmjUevnWd#djl_bd5`E%>Bh*n5~D2_-xrB*HcIuo$cnS+|JSM|a0@ zc6tQCKpAH5#XWOkIp=WAK`FWzNVGtVr zb-ue@uy|6b3X$ft99dKClABTGSnKzyJTBivP`us=WAa>-I0J2sJ&hB|x+K|xvL?3| zRU$+_lTtwI1I12vgdKcF01l=BLqw{*sXA7^BvV~Qa2knvm*0GUV(HIuZCs#?0$uMc$|0zZeQ+q|1#K{ z0qMN9MzoqjmriCc1826Zcr~1-M(9Li<}sY7P6%IhCP2;L4Z5qv?7WgVzE;5<`fQ^# zzQ)Y^*(NA-_Uc1gCs*7ix8|ZN5x3@+?l|-0G9CEE6XyIhc!Xmo*EHf*-%yvl9Kno2 z>8+>kmiah)?DA!VX!;=6!JICJg=zjhtpo0&wuOjR#{kGo1#JFrJ2(*KAPHU56|llz z_Gm*cRl)_z8t7%GdUmGgpYv<|S-ZqdnUc>|FJ@HJ8?p6O-dnKV$@Pn;1q{dy*NCF9 zktgE=g;ktp-2kRHPAi2`@FVkx!`0NcbHv!}LJY*8Ngv%%&!%kJ8`{VfBo9-z=W9aVvXFINQmaa=}TS`0X5Dw9;^LN`4E?c#Y zC-qNpzdBB8p5ojy;;Nf!G9IauLTY$fze;x2vw7XoVREnD2pOQH%;|cR;`nXZJ$aQq zsbTE;tj|?#X`rCOJoqxl&QDR4B22e`a-xjxtK*EOdPB14zsVB3UE+aP^=<0HWD@P)=b ziW=ewmAjX>U@Ck|(tpsHhL;Zjs|m(nTxv+u5F-njmu91c$8Fch>tc*U!9^+CXO6`O zBa%hm)ETRl>l9{)thV0%DWrtX^8FLGu5AC20RM$&I+!uOL=KOcVwADJQ=?o{_7RNy zgZ$kLs z7px_AhE?!WjYI2nHfWs74{MbooPr&S;hj*;dc|@t?)c6$ZQzpsKKZctf_Y&7rQHl- zW^;yL73XKkHT;7$0*Occ9$`Opx2QSUQow1ON=^&pEWgqljN}JZvqfWd2vjY#DF;18b@l0%>@%UijS% zLc@i_$AK%XaYoe$D$sT*(YH)IB&KwcY#c1NJx_!$?LszLG@8&MOK2-o;TtP8*|6JN zHd}^?@>)z6NwDY1Iq89ZdQQtO0E-)^WSO?ozU)t&oH&SwKt_6?k>2?P$(KXmAVxTH zauq!aDZ^SS<#`lal{T3~X4Z3&o0Wz#A9gAOw^P*@koss~)S5+O(-`=XQuB(Y z!H7f&q7hNgSMfbbRih33b-WD(dy0X|$Rc8;hU`9e+Z6b?8u#Sc=V@}+N9e!XsPeT3 z($e$%l;nBS`af)x^@oin={e{ASSP2rPDB!)5$3Vg(^+0g8TZqF1(QlmEQ?ZqTDA7} zHGFRCGMpXRMw_RTygt&)m5vL%lM+=~H0NUk-6)Irumtop?#C~_o~+%Hmdg2Ln2;G; zq-r-DF^D1^CG-V2-FG4%zNjBL?Q+p9D$CCZCDgr^q#&cjDRkN@R#pZsiSxPp%;WQ> zSA%H;r+kAEHN}?M7a`-7htm@#`w$*_Je!nwD7H2H${~HIQP1-&j6Rt02dBbVj0j`} zvobkiGOazuudSRU!{N4+m>%OO)CohEAP%m%$?OUlCE)p7HP`w8yzq>1Enx$4WnM&B z{4+}iMCW5;%9wPCB$ncZcfn*Ic5lYh+HWL`AO*4ci|4Sgz2`*QQif^RtGQ^TgrgI2 z4u|t^E$DB|Q!S(Fs6MO*>~bIPn7DjX@7d@ggh0$8Xh30Gt{W`U zO}Z-2sY@_3O+KpH{4BO;W-SC7HzqtjwG~(jaSXBDg#^!z++5QcR@-AYGThouwh=uS*ct84E_$|J1!Z8}!d0C7}U7<8%*>(oTY*mS$DE_q&{@jd_D1ujZ zjMZWBylzk`&SmORQ(=sEK`1->aUxGi$y@!B=~N!u;>>06IiXSk2X81&gx#->i1M=? zSFqJYseSQiZ#vm{ff$OQY3AS(S134;+m>SP6zsTxVS%mp3(@KAixujhG+uNTd>%(f zN``;ZecxmX{}DZl0ef{{x2BCGB~ylgk$ekr=OBWNE4iFTY5^FW0nDnB9a5HmtKA?~e?f|9I`nxh4>nx#*8KvcxQg2wo^ zP<}Cj@PE|g`%1hH*6)org%!Jw?tH^?#gO3pnREbb=nFGpcizk438p6hKIBdO)OAe~ zPyjMY%_t(20+z(Riy|6MgQWm2G70k-TIew{0uDsqAnRck30|aZ@WxBn6n;Tq@Y)Q? z6LK@m`&gCp?P;ob6kZe*M>%ogux}gZFI7U_a?0Z>DeDpvGYdjbj2su@g zh+f5k8sa8Dh3)MjND@k7dkhp+qlgn7hDeu6fTcHHwhC3p>_5X>#`X-Urvv zX2z2QAbx9^sxQv@C8ijee2~OGtFM!znjbt-Q(QaOh`ZaMYr5S>r<0C5SEA1fx)|rY zgTxC)&$IM%3E|mswUbt>Mo2YE4OeY7S7)_GwOPB$C0n(x+#0?eW!h$NHSM|$^OZJ& z$tR6G&_TMc6Q!jzT+1+Ktu5CGX7y#>%f{-h-eWDcEM+TtW-ARTvm*kmH70cYu;!B4 z?+5pH2;UGGmIzUJ2$B*@67u)QdD2Xk)lh|y_3SKPEL=v_MkKLPxQ42k$sWPCj=h`3 zmQP`}4`ZQH!N4xi$iAdW@v+QSRrV}{K2)R8=TQ8_vSXbwb&^b71|?mP>nfW40-#cK zNqb<23-JUA-sTe~rHs;{g| z^VrqiPEJj7m3FJ+*i?i!{?vH2@&?ZpE=JwVe-p!41982i%u$uq?$iq3wLYdr`xQGj zy(9Mc%qKJjbf5|K3ylLD;EC(u>Vy!U}nkp1w!l z7M&z|o8tJ1c~Oqy-cv6}k~IO?aM;x8w}#<@yfUWi08W2)Fg>l3hO^fG1?79JQibCq zXvUhPLB}p@fcthac&HK_Kb1csNlnwz^{Vu<9tvTwj~e82th$k=cgS(&cgIV_NI+#r zVXw#r!VQOqR(|3sO-e9zd&!9Z(UlUBvb+buh(WODrRTVE3)(@RmTtrk(yyO_wQGHR0+q2fbAl6LK3=3bg5bkC-8f*=zXsau7YxdsSyNQ3KeTwZTh zoiYy#H2qi19jbi#tZg#==cyY8G?|syTkFa-R%FvLWQW|hKB7~#=g-AOO~)0=!^Ni1 z;hFfEFO^qS#;*Fl9R0pFjCTnq%F|p7-5TNJ4E`#d;p|NAz+y4sIiNo43^03wpzmJf z>Yc-rvF0Y%a7i1lqfV{bLvA`1BKKA=xPaOH6RSKd0Jml|CEJMxT z<>LcAU@uVa>zMhH8<+ z41Jb|z^!+ctq>r=lhnh~UViWoG(akhBolEwZe8OgISt67io}>kK;OsSYvq9|My3<- zU*wke{vf zz9CPEyg}L|TNKl1Kr)AHgJXEyWdESK9hJB?bXD-iqMF^-1n*$O20w!ozpG};H~*E% zH@BFxGI`#B+@gdF@6qj79c67RLTR6@-PUZ)I5W*#3)6bGtcnn1YpZWW>!7@0mN)Ir z=e!xaULFdu`3&LfgXh!TURl$ll$u6n6DR%Y$o;ZPRcvpQv$Th{QS(PaSRr)Tue@Bwj`?N z=tMg0lCsRo(zvkn?nXx^x6|cc)gCypr^;6*m#OhxfIrUy9Y+(i0&;?}S_y=?x>^cjB+4&RBKI->S=E&w|h z165B4fV19rv-yy;Cf~z^Hedias91ba92VLg;?IyM=JR?f^L=QAt=Eg5W$y+l<1tw# zk*PG-75DM$hnC0;xxLmF>4tSBowv0{9EPx1mWdo?>FxAcQ?N2jFnMACgl!w7q21?Z zoPnSovcIckfp8z>9zn4RNO3ikq%N-#&#o|Vq}(rn?z}kv`^9Xmpg_y!m?MZau%ac%Bw~ z_1Ji$F1E_hE6F7h@1QHu-#4 z)sFiJ_FI_^;dUqm(q_VS@9s`6L=`n{q>_vLyKHZHxqLX~c+QX_K5jNr%>W*n^m3%d zwgvit1JUOvB9voEP#4vxQTW9ZEQT`XXehpY>dJL zueZR#*^_=s;0;I%1c0q@`*o(Sa}uw~BxJZiIMfp3QXG&6qBVOa5(oElZSh2Wd6YF# z-<)l62@SP_WnZN*{{D{7V|pP*;RlQ7+2B)MAOt4o0rcf>!% zcjB@x({`G>MAcfTW-oAKBl?W*HvvLGLbU3((WzpiIzWyIHn=MA@-NcsjV5zp2FN?R zw!TPFTXsuL5F;u-MxT8fF<&>jVCDGb>9Mq}~ifiEPQoHQj7-l$QA^*6^M3LE

yRU4Lzx0y;s|VVgn8Sy zl6_&8ZPh5~kV zh;PX;rfhH)@(MX=2fbH_?CW^Lec2fj@$jG>!^-&D*wOOb^j!U z{lT&RH->vNbgvl3^AI3~1_8nO8Owf(eP@pu+uK`z9|u1O@Ab3oc0o*Nkn0-nPxax+ zh{;|IE31txO+i<$pXa@f_E{lZ?Z6PFR*vUeha442zmR(~deVBfKxt)_I7akB#E=S3 zt)XUoo!{S6Iv^go1}y{lt~(5(@n{E~*XWpXrVgArjhH%U;N8X;H#)*qAQ*dgojpx~ zx6!fMywA&i(V3wwCfTOPG|*AmBsjgi;dPk>1KY*KJ{KT7Js zMA>|7Sa7#4B1gi4ZY`>TR#rz8W8-2!5%cVP}OLi@Ho5U=VAmMMRif(1{mI>1=jv%zVhPEOnHy(flq`UW}rd zrGC?lK|j-8VO)Hd&rz+ zYO%~X#$EYpXKw4k&!MOlD8tqVai&sH3AwI@oLpri zGyMmk!}1F8r^lL<98{p5xdvXLZ_pyz4xS^2#;%5e%4p|mLzs{l#ZAyt6xDazfWn>(_KG2L5i zRA~{B&aR1DnI7B9z)FACQab80QeA#LO@Ug~LZvSVtA3ymIRzt{18|I%0FCLO61$@$dn`j?GZ0;7q>gSeK& z*Rp17iZY;JXdwT6;OF_}JzwAB-uLTI=lG}2&uz`$4*>m?{a%v&rSro({(qHycfUWc z@0GDW51zjQ?f<9tJiz|AzN=|Jt38jk|4-rX^?3hO`14@R-`n;3(V9PN0s+bWU$tKc zZGP4JvtxcIZ~p=ac=mmNweL?V_b>H-MyKy|<)7F0c7D$h|FEgQ$jg6!_TTBte`7@UJL8 zHZp!k`F+9n7fKEBzoPtD{{0>0_ob;{DDve0it=Nj>UWgiC;h)rc&L7${8|F|UG?|r z&o5PL+JBu4{SNSZp8g8}oc@1|?T@Vem-atx?e8vPjp4_W|GVq^Jv01ODHP-XsQqv7 z`^^*nG_>DyqF*?wOg}sB_YCP*1AZd>9&&#naI*YB_}^zxl!5$yn)7+X3=8Dynb>*8 I`u*Gg0sczbegFUf literal 0 HcmV?d00001