From 46a29967e7a1d75d0509f373aacb18a7ec38d488 Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Tue, 9 Aug 2016 17:58:30 +0200 Subject: [PATCH 1/2] FEM: ccx input writer: reorder defs and def calls and - fix presure and force are written for thermomech too - change some comments - fix FEM unit tests --- src/Mod/Fem/FemInputWriterCcx.py | 142 ++++++++++-------- src/Mod/Fem/test_files/ccx/cube_frequency.inp | 14 +- src/Mod/Fem/test_files/ccx/cube_static.inp | 4 +- .../Fem/test_files/ccx/spine_thermomech.inp | 2 +- 4 files changed, 90 insertions(+), 72 deletions(-) diff --git a/src/Mod/Fem/FemInputWriterCcx.py b/src/Mod/Fem/FemInputWriterCcx.py index 9f02a4583..90da6fdd4 100644 --- a/src/Mod/Fem/FemInputWriterCcx.py +++ b/src/Mod/Fem/FemInputWriterCcx.py @@ -93,28 +93,45 @@ class FemInputWriterCcx(FemInputWriter.FemInputWriter): if self.contact_objects: self.write_constraints_contact(inpfile) - # steps and constraints dependent on steps - if self.analysis_type == "thermomech": + # step begin + if self.analysis_type == "frequency": + self.write_step_begin_static_frequency(inpfile) + self.write_analysis_frequency(inpfile) + elif self.analysis_type == "static": + self.write_step_begin_static_frequency(inpfile) + elif self.analysis_type == "thermomech": self.write_step_begin_thermomech(inpfile) self.write_analysis_thermomech(inpfile) - else: - self.write_step_begin_static_frequency(inpfile) + + # constraints depend on step used in all analysis types if self.fixed_objects: self.write_constraints_fixed(inpfile) if self.displacement_objects: self.write_constraints_displacement(inpfile) - if self.analysis_type == "thermomech": - self.write_constraints_temperature(inpfile) - self.write_constraints_heatflux(inpfile) - if self.analysis_type is None or self.analysis_type == "static": + + # constraints depend on step and depending on analysis type + if self.analysis_type == "frequency": + pass + elif self.analysis_type == "static": if self.selfweight_objects: self.write_constraints_selfweight(inpfile) if self.force_objects: self.write_constraints_force(inpfile) if self.pressure_objects: self.write_constraints_pressure(inpfile) - elif self.analysis_type == "frequency": - self.write_analysis_frequency(inpfile) + elif self.analysis_type == "thermomech": + if self.selfweight_objects: + self.write_constraints_selfweight(inpfile) + if self.force_objects: + self.write_constraints_force(inpfile) + if self.pressure_objects: + self.write_constraints_pressure(inpfile) + if self.temperature_objects: + self.write_constraints_temperature(inpfile) + if self.heatflux_objects: + self.write_constraints_heatflux(inpfile) + + # output and step end self.write_outputs_types(inpfile) self.write_step_end(inpfile) @@ -301,6 +318,15 @@ class FemInputWriterCcx(FemInputWriter.FemInputWriter): f.write('*SPECIFIC HEAT \n') f.write('{0:.3e}, \n'.format(SH_in_JkgK)) + def write_constraints_initialtemperature(self, f): + f.write('\n***********************************************************\n') + f.write('** Initial temperature constraint\n') + f.write('** written by {} function\n'.format(sys._getframe().f_code.co_name)) + f.write('*INITIAL CONDITIONS,TYPE=TEMPERATURE\n') + for itobj in self.initialtemperature_objects: # Should only be one + inittemp_obj = itobj['Object'] + f.write('Nall,{}\n'.format(inittemp_obj.initialTemperature)) # OvG: Initial temperature + def write_femelementsets(self, f): f.write('\n***********************************************************\n') f.write('** Sections\n') @@ -375,9 +401,37 @@ class FemInputWriterCcx(FemInputWriter.FemInputWriter): f.write(self.solver_obj.IterationsControlParameterIter + '\n') f.write(self.solver_obj.IterationsControlParameterCutb + '\n') + def write_analysis_frequency(self, f): + f.write('\n***********************************************************\n') + f.write('** Frequency analysis\n') + f.write('** written by {} function\n'.format(sys._getframe().f_code.co_name)) + f.write('*FREQUENCY\n') + f.write('{},{},{}\n'.format(self.solver_obj.EigenmodesCount, self.solver_obj.EigenmodeLowLimit, self.solver_obj.EigenmodeHighLimit)) + + def write_analysis_thermomech(self, f): + f.write('\n***********************************************************\n') + f.write('** Coupled temperature displacement analysis\n') + f.write('** written by {} function\n'.format(sys._getframe().f_code.co_name)) + thermomech_analysis = '*COUPLED TEMPERATURE-DISPLACEMENT' + if self.solver_obj.MatrixSolverType == "default": + pass + elif self.solver_obj.MatrixSolverType == "spooles": + thermomech_analysis += ', SOLVER=SPOOLES' + elif self.solver_obj.MatrixSolverType == "iterativescaling": + thermomech_analysis += ', SOLVER=ITERATIVE SCALING' + elif self.solver_obj.MatrixSolverType == "iterativecholesky": + thermomech_analysis += ', SOLVER=ITERATIVE CHOLESKY' + if self.solver_obj.SteadyState: + thermomech_analysis += ', STEADY STATE' + self.solver_obj.TimeInitialStep = 1.0 # Set time to 1 and ignore user inputs for steady state + self.solver_obj.TimeEnd = 1.0 + thermomech_time = '{},{}'.format(self.solver_obj.TimeInitialStep, self.solver_obj.TimeEnd) # OvG: 1.0 increment, total time 1 for steady state will cut back automatically + f.write(thermomech_analysis + '\n') + f.write(thermomech_time + '\n') + def write_constraints_fixed(self, f): f.write('\n***********************************************************\n') - f.write('** Constraints\n') + f.write('** Fixed Constraints\n') f.write('** written by {} function\n'.format(sys._getframe().f_code.co_name)) for femobj in self.fixed_objects: # femobj --> dict, FreeCAD document object is femobj['Object'] fix_obj_name = femobj['Object'].Name @@ -458,19 +512,9 @@ class FemInputWriterCcx(FemInputWriter.FemInputWriter): f.write('*MPC\n') f.write('PLANE,' + fric_obj_name + '\n') - def write_constraints_temperature(self, f): - f.write('\n***********************************************************\n') - f.write('** Fixed temperature constraint applied\n') - f.write('** written by {} function\n'.format(sys._getframe().f_code.co_name)) - for ftobj in self.temperature_objects: - fixedtemp_obj = ftobj['Object'] - f.write('*BOUNDARY\n') - f.write('{},11,11,{}\n'.format(fixedtemp_obj.Name, fixedtemp_obj.Temperature)) - f.write('\n') - def write_constraints_selfweight(self, f): f.write('\n***********************************************************\n') - f.write('** Self weight\n') + f.write('** Self weight Constraint\n') f.write('** written by {} function\n'.format(sys._getframe().f_code.co_name)) for femobj in self.selfweight_objects: # femobj --> dict, FreeCAD document object is femobj['Object'] selwei_obj_name = femobj['Object'].Name @@ -478,15 +522,16 @@ class FemInputWriterCcx(FemInputWriter.FemInputWriter): f.write('*DLOAD\n') f.write('Eall,GRAV,9810,0,0,-1\n') f.write('\n') - # die grav (erdbeschleunigung) ist fuer alle gleich - # die verschidene density wurde in den material sets geschrieben ! + # grav (erdbeschleunigung) is equal for all elements + # should be only one constraint + # different elment sets for different density are written in the material element sets allready def write_constraints_force(self, f): # check shape type of reference shape and get node loads self.get_constraints_force_nodeloads() # write node loads to file f.write('\n***********************************************************\n') - f.write('** Node loads\n') + f.write('** Node loads Constraints\n') f.write('** written by {} function\n'.format(sys._getframe().f_code.co_name)) f.write('*CLOAD\n') for femobj in self.force_objects: # femobj --> dict, FreeCAD document object is femobj['Object'] @@ -526,6 +571,16 @@ class FemInputWriterCcx(FemInputWriter.FemInputWriter): for i in v: f.write("{},P{},{}\n".format(i[0], i[1], rev * prs_obj.Pressure)) + def write_constraints_temperature(self, f): + f.write('\n***********************************************************\n') + f.write('** Fixed temperature constraint applied\n') + f.write('** written by {} function\n'.format(sys._getframe().f_code.co_name)) + for ftobj in self.temperature_objects: + fixedtemp_obj = ftobj['Object'] + f.write('*BOUNDARY\n') + f.write('{},11,11,{}\n'.format(fixedtemp_obj.Name, fixedtemp_obj.Temperature)) + f.write('\n') + def write_constraints_heatflux(self, f): f.write('\n***********************************************************\n') f.write('** Heatflux constraints\n') @@ -542,43 +597,6 @@ class FemInputWriterCcx(FemInputWriter.FemInputWriter): for i in v: f.write("{},F{},{},{}\n".format(i[0], i[1], heatflux_obj.AmbientTemp, heatflux_obj.FilmCoef * 0.001)) # SvdW add factor to force heatflux to units system of t/mm/s/K # OvG: Only write out the VolumeIDs linked to a particular face - def write_analysis_frequency(self, f): - f.write('\n***********************************************************\n') - f.write('** Frequency analysis\n') - f.write('** written by {} function\n'.format(sys._getframe().f_code.co_name)) - f.write('*FREQUENCY\n') - f.write('{},{},{}\n'.format(self.solver_obj.EigenmodesCount, self.solver_obj.EigenmodeLowLimit, self.solver_obj.EigenmodeHighLimit)) - - def write_analysis_thermomech(self, f): - f.write('\n***********************************************************\n') - f.write('** Coupled temperature displacement analysis\n') - f.write('** written by {} function\n'.format(sys._getframe().f_code.co_name)) - thermomech_analysis = '*COUPLED TEMPERATURE-DISPLACEMENT' - if self.solver_obj.MatrixSolverType == "default": - pass - elif self.solver_obj.MatrixSolverType == "spooles": - thermomech_analysis += ', SOLVER=SPOOLES' - elif self.solver_obj.MatrixSolverType == "iterativescaling": - thermomech_analysis += ', SOLVER=ITERATIVE SCALING' - elif self.solver_obj.MatrixSolverType == "iterativecholesky": - thermomech_analysis += ', SOLVER=ITERATIVE CHOLESKY' - if self.solver_obj.SteadyState: - thermomech_analysis += ', STEADY STATE' - self.solver_obj.TimeInitialStep = 1.0 # Set time to 1 and ignore user inputs for steady state - self.solver_obj.TimeEnd = 1.0 - thermomech_time = '{},{}'.format(self.solver_obj.TimeInitialStep, self.solver_obj.TimeEnd) # OvG: 1.0 increment, total time 1 for steady state will cut back automatically - f.write(thermomech_analysis + '\n') - f.write(thermomech_time + '\n') - - def write_constraints_initialtemperature(self, f): - f.write('\n***********************************************************\n') - f.write('** Initial temperature constraint\n') - f.write('** written by {} function\n'.format(sys._getframe().f_code.co_name)) - f.write('*INITIAL CONDITIONS,TYPE=TEMPERATURE\n') - for itobj in self.initialtemperature_objects: # Should only be one - inittemp_obj = itobj['Object'] - f.write('Nall,{}\n'.format(inittemp_obj.initialTemperature)) # OvG: Initial temperature - def write_outputs_types(self, f): f.write('\n***********************************************************\n') f.write('** Outputs --> frd file\n') diff --git a/src/Mod/Fem/test_files/ccx/cube_frequency.inp b/src/Mod/Fem/test_files/ccx/cube_frequency.inp index d73be7173..9e129a1b6 100644 --- a/src/Mod/Fem/test_files/ccx/cube_frequency.inp +++ b/src/Mod/Fem/test_files/ccx/cube_frequency.inp @@ -488,7 +488,13 @@ Eall *STATIC *********************************************************** -** Constraints +** Frequency analysis +** written by write_analysis_frequency function +*FREQUENCY +10,0.0,1000000.0 + +*********************************************************** +** Fixed Constraints ** written by write_constraints_fixed function *BOUNDARY FemConstraintFixed,1 @@ -496,12 +502,6 @@ FemConstraintFixed,2 FemConstraintFixed,3 -*********************************************************** -** Frequency analysis -** written by write_analysis_frequency function -*FREQUENCY -10,0.0,1000000.0 - *********************************************************** ** Outputs --> frd file ** written by write_outputs_types function diff --git a/src/Mod/Fem/test_files/ccx/cube_static.inp b/src/Mod/Fem/test_files/ccx/cube_static.inp index da7c57152..7706518e8 100644 --- a/src/Mod/Fem/test_files/ccx/cube_static.inp +++ b/src/Mod/Fem/test_files/ccx/cube_static.inp @@ -485,7 +485,7 @@ Eall *STATIC *********************************************************** -** Constraints +** Fixed Constraints ** written by write_constraints_fixed function *BOUNDARY FemConstraintFixed,1 @@ -494,7 +494,7 @@ FemConstraintFixed,3 *********************************************************** -** Node loads +** Node loads Constraints ** written by write_constraints_force function *CLOAD ** FemConstraintForce diff --git a/src/Mod/Fem/test_files/ccx/spine_thermomech.inp b/src/Mod/Fem/test_files/ccx/spine_thermomech.inp index a72e04979..edcec7590 100644 --- a/src/Mod/Fem/test_files/ccx/spine_thermomech.inp +++ b/src/Mod/Fem/test_files/ccx/spine_thermomech.inp @@ -137,7 +137,7 @@ Nall,300.0 1.0,1.0 *********************************************************** -** Constraints +** Fixed Constraints ** written by write_constraints_fixed function *BOUNDARY FemConstraintFixed,1 From 49c739e15e24a372c3c0390ab84cd5cb67370726 Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Wed, 10 Aug 2016 12:52:52 +0200 Subject: [PATCH 2/2] FEM: example 3D, update to get rid of object error at file load --- data/examples/FemCalculixCantilever3D.FCStd | Bin 29897 -> 29890 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/data/examples/FemCalculixCantilever3D.FCStd b/data/examples/FemCalculixCantilever3D.FCStd index ad7a9ebaf25ea37036b5dba885ba80336606c382..28bff09fdd4df4048a0edf857cfb6f24dbff6be1 100644 GIT binary patch delta 6168 zcmZ`d1ymI8w*x4R8(2LzNRz#c% zoP1X_XvS!~D;o}sbzSkQ#9p|zUbR2>_x(c7y%WdA0(ZQ--6Bo2K{+s& zz;-2)&(?b%!8|eaT=4w3Ev)U{(7Ir`j>e1m$JfU~C6hIRC^uk@WMiE8Hh4dOMny$M zNIKmTR*13~fH#!5FMExBJwVxrpRRu;u8xvV-%T^I4GksRv=JlM`R*fyGRV&|_T2g= zl)b9p6<9kWy;fP#)L0SFG*qtB4N|Y_<(=(7=kJAhkE?q4b9rxmOOb5RiZIx%*f_B5 zy`4RmH5DPtgb{CzMV zq-sBPDE}_@(%sGJ8ma;5oNeLdFxFP%D9x0l-h9$)U+SstP38-43H#A^l1B+T3Dol6 zJ(l{MQahE8`4t%+D8f`;3ywtK7z+}k?ovfI)loCEjF^I75+jTy8yjS%^=T{uC1Te{ zyvXD#b&BRy!fKrrq87JF-+(_n;Xfam*UZGN>Sq~f2=m zp3Fb@!D85t)4oLG($CYL$}i`uD&^~r^loc~siTFY z!EIVT144y}9;fGlz8NM%4%r+n(vp01ct6OM8fkJpu`?Z~}{Qxj9aAZq|-~GvK zvHta{ugUzwEm-GBw$sku56Ph_V(wda8bHL$QoTHlVh>~V6PoiE`zz=NAS%^B{+xmUBS51{2sQbaLg zDw(?EJK7=C^6Y}D_C@7dLH5M~%?pa3pMUyH-xxUvIEuTgpqLmRG&Q(j8THc2jMq;EjUa2TUw)iEJ5F zsmiGPCUQ3uWi7rajvU~gtDReIjtJ1w5SJr_XFO!cG8HIN-?5Wj1=jRP$e!y&Vj*4o z&6k#fRQa3_`;za62fgZ<*1l5S?Y#>x>M?8G3gX8OVNtG!#`*8(8d%S@7> zD>HR%x6L@}(exg++A>~EBjpIU3Qkkx@-g~l>MBl3t zbGna^ka^fU$iuPmbbg!{m5sUgxovKS9^)sdSV*Jo#Va`)8Iz)pz=)dfnuDqq_SF`P zM{+^p!l9e9c}zB0*+&DKPo4~#CD=33@gxK*+vsqW<kE0d| zLlQ4^A}g7|YYju>&t`Kz-B@Rw^4hYzRnymvq7HqQ2v*M|=o?!(DC0`eEDc5+)JbT! zldo0gM-T|&iCS!!5^VQAEnqfeWDg`4jtjcUG*BU4x?~3t6v_CqcJ`g`eU_(A2yU;x z=sB~HLRekanYK?)Pp+lyewk?3tdS`)F@EkxW}yfjIJ(5o>B(>YG1??2T;zrqTRQ@9U8~*jC4m<4@pG8D%W2tQW7iD%g z{w>s}1CR(RT|^uH=W_)MjbF-OAXR2Y4cA&6#i*7dJ~P z9R^|)(b???C+tDGgek@EIBX6P>p^V>74O5vars09D~rp%tcvKK3w zYR%2LrAD1T>gh7xMX69>xf{``xh9Hu{qBl8LF2YWGHK<6E_&+z?8%3_9+q#P9V}P& zoVRn_UXV#HGB!#$-d)y?nNm;s^5$oT09z#ZGJ4J+t-fL8z1khK-P6IsqoXmIpBs_= zTb;kGRJj9l&yEK}vb!}{iZ^G!z9EhT*GAIlU2jX|NvrUP#*y}oS0%fe6xY*_Wo_pd z#~Eo~HOdk>oH*XdseBT|@ytUPQ*VUarmo%S?zQ!07s8j)*U9yCI|^Rqq!`&7&bvq`*YN09 z8dLvSb4piHl$|G=te8qz@NE63pt8KVN-E&yU4164<4O5Iv}0cg|1arfw>{!%w$16U zDSPx?RW#lGnvs=_CDp94BSl9sM4f$}VAFkLUUGE$!&w_gAB)8^gOmD1{)a(M{=8q) znB$qCpBF10y?=BUM5@#1M58+t))xp%XoyS)~OLSRTZp$yQ40 z8r%vocs~%H`TkuFbwPo5pG@37%d{?HzR;|@1@9g0oXSXvXWis}>kZAAt>&J650Fg@ z8UKKKKQb=KK`$u$iOe|Zc>va~Bj>%fy{BHB_VQ(PNU zJ&(r>tN+1Edaz3X(J!QRPbJaM5U>9+x*6mHD|^^t+j4AFG&Ia0|6IelqsjPKqqw1} zO5yapv|EUU*)!9^qrg>w2QQ~$1RP)ArmJmKI57zWQB9z%$JFkAnal!B56{qb;_fBG zp`7wVU6c5_0m`DsC0WR8q7aed*WbHx0%7A@9D;fNW~SV)PixB(q-;(`nW(N4@@7Rt zQa5;VxjmC9g3tnS$8omO4{c$}rC07zrClx3?K+oH>tNP1nS@F8d^~eNfod{uaU_|* zk8W!{;*btrAA^EK(pKBYf*P_kjV@v_OjFAgn6@(>ecbG69u>{x$#Xl>DN`9@-_s%H zDN}V^XF=G`uv1DzR3rjJp?aSqNL77jxSaEPiEnPkFXzvvM-yHaplOnJ*+{iZ#&KvD zWg_Z{d-u>i1J)k$xApzJU;=S>+^}@_!9)y!Tw@%#(?Cy&Eu-9VQ~8>jy9SO`gCgW7 z@Wr;#xHgmM+lVtR`Bqvjd|_0YaO(fgtlbx)xCuccDU=K7ph z>BPBGW{a&GX7EPpYz;dYcDp`d@RF%&>83LyS`#@(o?lzFyNTv`4r({}TNv6st2#CD zDs{E@-To;vu=FZqVFM=E)Re`()3L6*CaA-u#3usodBXv|?$!sYT*gB0? z@8LSUQuXA0WP(4D_;}@A{oU;wZtD1up#Y_wZZ3m-Vt?Ge=irW=^u%$`_~Xy+s!DqN zJ5DI7`3*@5iOs&8NzmbN?W3P*?7&)-{DJR?@Va9v!&%FW`u=#3V!u=TZRe~idS&gx z;)ThFopqt=d|*5yZbT$SJ`ufKR29r6n`CZGBW?!$Jpn>Dl&$xODv!b#Q4VAFq720& zb*Iu6(%ZC7Z{e|4}(saV< ziL?$kh*+6cr+A6V)LN*BuL!g67)mRdwi{O>ICTtTq8s^?)LYR7=@a6BHteH@#B^{Q zc1c-bh7Tj-r%ub_*$E}r9A2Rf^XqAyoaW3t1uDLohfem-U63f^BQW{x-l{^mg zIReWfen^Jq8j_g_T)2!Aj#w2|vZSBPGay+K=wkwwulXVInrm=oCa~aJfY=4Q4(Kxm zmIeHfG|e?cGZRB~NdO|N zz*q(j9CK~vf)HE9pQF9|fGnhg?ZY)TFZ^A(7pxQkVh_j(WEt5)QSTo>pR%(_#gL^b z;_Gmtcqm#S1Z-ib_Ya{f>}=jKWUY$$mYk?N6s>dwY!RsU^3bOoZ1pi@bBg##PLu>l z(aJ=?7KM7R0A1l=JB=a3?kr}*OJ;|%5zI!Tq7|V}IN5~JWXwwVF`OtR3Md!BEE*N9 z1pUUz=87hhQNmB+% zk0$d~!f)Y3nNvV-6U@FwMXN%efZS}t7_v7?_@6mZFbe2hg4sk=v>NmqH=8SltWgPn zmJ{Vd0Tm~hO%``M=1mg8Ulu0ogRv$L-WSXEhpXevCgZk>lc13F_{$4rd*i*SvcUhZ z@qd_t|JcS_X;INZr=bax>%1+xJ*_JX8P5xwXLI30)|c!Vh)TW5`>ZTR%kV za|=7}bP)1MipZR3?j5+)(E#k$?>k?SF?MYG&K2)WXf(Ec51~27`d43F=1t0igLBbf zm%X8orTvXy=6FOeAtSHbC5+|uyHudzYr?%mM(^CFyhKRk@$oJZS~bW&$Sqx=e-Il( z`bz{8XmNU}5@1RE46-!Q@`_9J>#Nx&orNvKjXlO~faL@0a z`pzdDej4I~60vhG79gFHhQ->9SDk z*J4GAfGp*9nu8NWjqr6=vbkM!xT*m20E$R>K7h4r#yuF~-6q^yWW zSCUzgae73$?9$AepVwR#7W<{kJlSLIMnJehqh(FyIA0;1t)1d|muYkF8e_FbrRk#t zEQm!^gn)R|;cDt9?QT^#r;@&Ounr^3s@EhsF7FFQdAnMYXZ3}=ZXxp7MCy9hhx&n1 z$3W~41=%TsMYcFLOP+_j+`9X4sy>brg_h=(B@c7@dr@Mypvgvy#RI(tYT`d7PC)h6 zhSXgx?nz4y_v;Fi>UezFTG||4iHbO8PEt{N$RU`m0(Bo;!&dwx+CWIYN8u5%WY{^q3=fr? zSLE7l(xBdtsa(iK@1U-`yG2(ND1#A0*_ckF#$e46-L1(10@PD1;Z(0~nWU11K1q3l z8#{=c;h8aoR-NM2HeLU^#~D$^@w-Mkhl!F9&Ya4jJ;r?DhHpH>5bR}~Z^N&Cz^d0b zB$4s1&!oozUiKT2h*wC-Bf>U~MY1;$H>~1A$D-FfeaWgxx*ng5i z>Sm+a9(U%vjnB-mP1NsEXSlOai@0d;6q(qR<@4OB zKJui;^l09$0z#pKy_@HLD4m|CFVe4v@W#SY&|tde-do$9`i*-EMVuaw7BU_NI(xDN zT{-f-&pD6@eFKxkOU5#q+vyLLdHF`+bK}B1zxir4`qzBE&FOY^86^NFdzrMsVlyE_ z7`fsv<@2`OAtawjy`Ma6a+O^<>o8wp$>R>uqoxkgX*}(r?G${*Oy5nUQ&P{EZt=#8 zPxxSyuKD|zv~i4!tU$EmHL;5*yVQ|gx$aG?9Lfw{$|GCb znE?{U9)d5D6$q6Ff)H7toOTumpUv6(T*p_lPx)8Ntd%7v-CcMFUmnVY2vmHzTT%72 z=13sb^7d-u(P+QQo2tVVlMb~oD^hyqtHA=QnM8S-lhBoXKleC1|M{CFqHPz$BK~&? zcb2g2cwC6na>md1#&-@IN0_`%hJ$urtK{YX%2_>nGfI3)ohME`0gk=dX3bta&YE=f ziC%!xte~l}tP=RA?erVBTlnkoZ#v*?N%h+s^x`UxXU(^|luQQD(D_pn->9>srmz~9 zH4|Yl0dO!q-{Y;XX0ZrMc|^LwG<*i%LM!9v;a)W`@M`H$Cm`HjU6OsME|Mh0*h&UVobJY*BQd?R{0Ff}9+`O(}Q~uS^VgJ2}ok4$$ z)zV{(^?rTGtT@=P>@|TnT-bl)&m8leP{L!`!GDHwF)5DfRZ;ONu92iVL1baY6lijG zvbV;lBOsZ+F%kPBPE6$E#MBYG$k*R)bgBPcRxAm}jnl$A-y}+=5Rx^|Klxbs4FyVy z=*GPgIu!hR`cBO5WrK&E=kpf?le5!0G556bTY%d6?Xr-Lj?IzmBnPVzf@$OI?NXw0 zDWUe0DuxI6A!NrJULcaZg7uNJ+_|| z#NRAOtBuX)45OANjU03Lg{#>1r57%lzdqbOye$$u$VU2>K|qY=ZWB){5h*EX{Qlvq zQPU8{w`w;%?&y^U;~sAZ{d$|8T0WB^{tUxW$Idb*;@m8Jrz3wR(CV?8lZ}LP%S}vQ z3paJIorC%kX5==y2^ExkyCA4V;YKQ#23VFAoVw21f3xP!ZTfD(jUnH$$N`>?H`#vO zy-`ZB4!Gg*HO}@w2rGWJWzDnP02k7JDc|iR)5QF8^uLeaaq@-e^>;0F*spTZMZw68 z`h+$;pdd1UMywK`K$+@SJH_xC`cPfMmyMjd8#%n=m4R=4G|G3y-HF`KdtQYvZ25Gv zaP+3cqww!j;(Q2+E6VZ|1tdoAImy(y-+LORvKb3idUn)3(R;2xoXS`$lnH*CpLf|( z2~M>bY)QCVWyGSOLZ_uuB`vwySb92!Hy;_O8-Y*H-%;CPh+N1DR;OI62EuL-hUrWBedLwuv{?iWL7_9YUk0WRme`$VYiv6LR9(9 zy0XgbRIQJpzECVvByAC==Y9dBDM#^2c*bvead8-B@|Dv>@G0?Ymt}D}bU(bexTc5M zIM_FDEKW36!Kh&A*C`GhT>HbW$N>*D@)O-t&LY)PW*^XSddQT->d*D;jP;Bf3|=ow zUo@NbX2b8tY&j;+yL9spt?dr6jq~{|&3Vh61`CxqR?tA!qAV zJX6|_RAKYz=Tx#|<9U1};qN|_+-JAsia0`~Dw|0py0E=txpzH1?(lU_zn3)^K8x++ zHyBrSGg1<{pKq+gB32biZ77Irtyx_*rP5Ri*|FcAe%k07`({)<9@+Qw(;y0POI0x% zR?D;yUcaHnHRjH_mn=U4KFwozRp#QkUK`iEQO8TE&o;GsHXCGR=9W-iLc3#l?V-r< z_74RrdF!vX!tC|*(>o<#dxHqJq~6{+oLKH_=}i@Ma9K=5Rnkk(D)u4W?QEJrA%3Ko zpfzdz;=!{!X%l>lDNE*-O4sjVvl|o%mygq0lcJ_wliMB-AQAJ))&?%tm==h8S91aXqF0$b0o`~R&+Xc9-jD!EqfQJ2OMsF&H&vfiauQdh;W{>4xH}1;`l6-gO;})troMq#vljD{#(NlLVw< zoR5(Df!56TSKVK(JfKV#G7S7?qo?QmMWZk@ec2~^Ke?~x_?q>gqI9B&W3qOzQP-HNWDRHQ1%)Bk+q^xt1jc@uO5G4>X>9+N#*Bd zXjkrJ_kzu?B+q^2mV76NEIi+`2^W0{@f-?4p63<)HRSI1@4d|9J{UMXYRpftI4&(E zp5EhCVkXR@?gncKiLJ~$Dci|JtEpblklGHl0CV|rvfpElT9z)$e$eKnEi+e+h+ar2 zszmSsOy37&1k#MCtEs%{hhp1B*j3Tf;g&%Zoot66*46KgVzd-(2tw8=hh|j$^oLBw znE#-umZ4mCdVz69C3R%iGB;#W7f2i^8b6@d0oto%P@wJWSs}&XquqzqC-b(yHs0Qq zcyINS!(JHKyV;w4Pu$4=*wa4U#Ygzw8G10%=IhZy@jh?ygrRFjvLn}Oy|EW+EOfiO zcPK-X%CHdFpIDqLu1X7`91*u6G%l<0;4%8yK6iV+6M^$^P@6jod6uR}xt1Nh4ZS#A z9EtQWcLUK!V;+(Tc9IJ3Xap%;i(n(#i$wPrQ}P2t9PD3eA@)UYUYc5AtDqgPdJSzpkt)Q`Ny&)qr6^^{J0y zLdR{;H!PfUc`G9~@dtu;I?S8$C`Oj;=T(yLj3t5x8M;pvhpbHN&^ni=^%);KHzxjk zR31`%>uX`!^a|k-`FoqD3SFEDS2Z5lxe32g+)I;ZLr%~AqDhi5`G~_=2YK|AK$bUB zXBNZtAo!M2f$4!X-($2~thLTiP|N4@8n223|4=Q6n#NSnR~97Q_~3dy{qi$n{)M5$ zOMqoz5l=)UmQpXlQP80RB^0KKz1|E%&QzwDbew78uyAJ;-C4erWm}}P+0p%^9&ON7 zNBChMJ*;|o-OV#sT_|RhUDfAI=G5a5(axF>>b0#kbN!yOUa8UwPpp;yLDX2Ew~viH zctYF;woXk=G2}I_tMy_$+fqYk%lpf%>2LTs)`*M^XuHLg_=ufemIS3dr}}oV_mUi? z;$J+xmdscELc@L&?VH!C^spG`YZ^b-MAB%zO%G2VX{i1CIKh&S8NReduDS0YhsXTN zo^;+HRBpbS$UKc4Xx0x)2Gmt0gN8>}v3Vx7t*n!@1 z-XCbe-}HnsV;i4mN+z|BCyjJ=#p&z@{#saT_hI9nw(e=e-BynHH0zJ$&HLoq{7s?& zF=cWu8ax>i-sZQOuTHT$86NH}lt*geB|Ko?ma*nag`{S+zrJhJcP8i4f7UsS;5ewP7_p;dS4`n6byl^;VrGP)4h1HzuOC1G!d3&h zWFhSDi{KT;`4LZbW^Yf6{;>BMC%iRDi)VDVNa)Aa>b@Y7zN*_Dm#z1ss8gvI+1QW~ zM!ZaG>?A9>b-UgB3DifxNmUum2{_|@U_n$;sW>K4j?&Ie|8CU9TKdDaVL85Rn7L$5 zvBKln&Vcu2$~2F|yXmYg?BZlp6e&k?qk?dPoI(6zNc?P#gVJ#kO`A|2&@A*yOV>hs zWcaZ_iHhr&;lgjpsyW;>O!@AYQiVgOI;pI9*L5<53><%|qR?qE!%+ht3PIgS67Aaw z{9M=1xUu3-b*pt|ktVFsWi7^Waz9gyG#}hna&(vwtRl>Nc7{q}6jXipqvD0A27|TB z{8Amcvm%<-P}`UJ<~L0S=9_)hlAH%0p9?Wxy;t05q=b&yeNo%GsVO(kpI{Y1w1aa9 zz@FgWP~pEa(KhnnXMx-5>$$NgE_tbRLFf2*IpKn)Gm;dKyoa%DU+S z-r~=wy!GYjp$l-KX{sffnZR>O-d^a8L;s$w{KIYGh_1tW!~))u&XnRFm*W19*>5NT zTV@DwA#187n3=$J+JcK7z%qQ!Y5WVG5i3Q41jS)a3@}kLSOx*C2N$*s57>n@dBJA> zqTv}~upVjHGE=}Vd6O4nM*NZ!1CA)!18AawG@o;*oou}EODOJH(MVDe31f)bdb6PS_` zm@*QW@)Nq=z}v2jb&^kYlFxOLFLjcyb&_v&lK)UJ35U->U*$u020-QjWC1`Z0I~!iD*&OQX`qopli7!1i9ECmIuju z6n!?T2?XhI|F5 zt5W?5IJIy835*TsE&$fY?Bqhl+no4MBEp7;-H;oUoCwPmi&JQyT%7!x>-6NZU}g9E_QIx`9<$u6Ua8PtW+W zz6;Mh&bdQsxs$fI7F*s>xwcy|@aS!tO1eNYvAwQ@c|zaZkQJwUtCy3)YinK&TL@P% zl6!#7h0frv)>BWUScW3oEfn{N18sqWZc41&ZACHh8D_0^&xP2o;4b@1U&HaAe95FG zN~=&tU7OK~BU3EIFw%l2esf-+iJ065I{O*3IWt8bT?h&HPVQ^nnK8W&Za5)ZE1tPc zlgj-ayme9@NexSCVRlbXI?I*Ws9)+8_Brw9y;&5FtO{v*iOKMD>P6n*1a|nfZZ?DRuTh+ zZ6Ia^HBMZe0y{JeyZSZeM!W#68N9O==i6i-#k*w@F>P$WS&7aI{{wonL!N{^!7@hM zpIdrJxrAHUP+!}xfg;e7VE>Px{zY%yY#9|IGScU2#&UUWrMGfcA38m@@vXLfH!rkN zXHO-Y^|%V_>16|wbl|H?6Ie&_ce^mX+amZ1q<)v?7Pb2L)rZ+PAS%PKi;MVEoOVB) zq_`_|zTV$YP-{A$GXB^ur&3RqL%(EswPSp6twf&nexLKv)2A$rb3q>$BvY9&cqe-PITb?+O#$z7+e znp+JJsS2$K^x$~S^{jS}!}oiHK%$Y^CuDbAXl?17(I@&DL(Zmh(MK8Zy>Q9q=ws1Q z&7QoPfXv{_8K!Vz8{%%eI`>9R%AOCzAEe4Kw|ZkxiDNwh5?xa7hj6H=bjI?S_ahZZ zlong{w3U68z}v4&Ne*!7b(oB?JcqxMWwaU_KCLYtyFy*hT&JdRX*Eb1kLgSD1IhTU&e-y2_Tc06hvlpJQ;%h1cwd#1Km zOisXiV3bp_rz!9C)B8rZCQpnb%|{Pu*0AFapYW?T`wSrSf$5fnOrbu0V#|?F_~+@A zoz{v@;nrp)&6&mV6ZTcrZyX^d#)$(9B2QZgMp5jd4JO9yBGasUH0@q$QesD~V%h`* zP9If3NecACdG(MIjnp$&?H1y}!d~1JYlCf(N@>($H-Gz7oyk2#JPFw|>a%mZ5q_eWYmxb~Nv z1YiM2pQ(jhM&tdjqmi>#t)j_ahgBr=Oeumk?9y)CZhLjh!s_k7%&@B=kse9M(3iT+ zAXXX;6V(Z?P~wJJI+9km>op-Xv#$C09ZyvUyyicH|L2{5x$t=#;YH*1uU0GC($dWZC5=^gqWOJIV{M#nU);|B z^klIwocJzQ#+;Dij^|xkbyJM1mbIx9>ZTdSNnII^a~1c$Te$@rNM7VM7%XEJ1|vF; z^S4w6CSV1e=`I|pIU|LKxM46gM~uCxoy)&nH2W~rXrJ3ba2O2#Z~ODHGLQ_L?Mx;5 zyLRNHrxO|fr94p4TId;^=ZQf(n4_Ic?NreY|Bgk*2X~)15AgdtFOvUAWuUtm45sC3>iWMn zuIS5>-{L>PNd7T7-#IsIj?16