From a471d1e8a2fa1af68ee947ad192c99239dc02e03 Mon Sep 17 00:00:00 2001 From: hboisgon Date: Mon, 11 Apr 2022 16:20:16 +0200 Subject: [PATCH 01/19] Implement states for wflow sbm --- examples/wflow_piave_clip/wflow_sbm.toml | 2 +- .../wflow_piave_subbasin/instate/instates.nc | Bin 0 -> 335907 bytes examples/wflow_piave_subbasin/wflow_sbm.toml | 2 +- hydromt_wflow/wflow.py | 203 +++++++++++++++++- tests/data/wflow_piave_build_subbasin.ini | 1 + 5 files changed, 203 insertions(+), 5 deletions(-) create mode 100644 examples/wflow_piave_subbasin/instate/instates.nc diff --git a/examples/wflow_piave_clip/wflow_sbm.toml b/examples/wflow_piave_clip/wflow_sbm.toml index 9ffdb06d..c4fd0d11 100644 --- a/examples/wflow_piave_clip/wflow_sbm.toml +++ b/examples/wflow_piave_clip/wflow_sbm.toml @@ -24,7 +24,7 @@ gauges_grdc = "wflow_gauges_grdc" type = "sbm" masswasting = true snow = true -reinit = true +reinit = false reservoirs = false lakes = false glacier = true diff --git a/examples/wflow_piave_subbasin/instate/instates.nc b/examples/wflow_piave_subbasin/instate/instates.nc new file mode 100644 index 0000000000000000000000000000000000000000..59b0ca0075da2b853ac2d6eb67c923d714441a5b GIT binary patch literal 335907 zcmeEP3xJGO`+w%PGrKc8mtC7XYh8BNUE~sF*w-$(ELJX|ZL^Ebx@6Zaic+a4`skx8 zrK0$vR1~E?ic(Q33Q-hAQ53~j|KFLJV_$Fc&h^af=)EAOUboy{`FUBLdUPR5fKxVBjyO>*H>+!RH;*dre$|65uR0O=^+4Qg0HNzs_Xg7K~jY=2mVapT996pbq@9+p31e9?%p{M{1c07M_v@%nxzjSCSJ4xJY&P;c2?f}o`c6PhZpCnZxd6KYW z6#PlTb_e67W2aL1aVM(lxiO4(2Z>yl_TRij9XttZ-fY~je0h7e?HG@`6iG1+GIm7{)kvkv^VU{Js>?gTZJ8eK7p((_|DKfAr3iZX!pE z-`Q2Z=TnVtuhXq4&ULrb=-sz-+&ZRMaLV#=bK3Ng~w9oZa{PcFFG2w@a_AF8MvW<@KZ1=+5rkg#~2=rA1{#>`|0mc7?cy zjwvl3JBCc`5y^cgN%i`$u<{c(1%6=v)!6(HQNNMJrK$M8AazVpS(cQYdcI=njT6dJ z$BZp24^1qXc~_tpGfS-r=P9al8ItQe12?2Ji+ zFaQ&a#xuNEUEUZC@z8cvtEO$6wrD)EaQGEhwP@Pn@}?~qe=zeBNx!M2J&UoyZeWwx z7?9(<)-`gRE!oJ+aoQaieR7;XvC%8X`2ia}a-7ezaZQf%9yYq>IES;5o#T{TBgdJ> zM&}%->#0Y_9OphZuF7$)XQO?N^ID$oL zH^bAnfv0ahPv108-`bwOH9dW+d-_)O^sVgao8sx4?CI<9^tF2WT0DIdJbg``zHy$u zdQV?%j+5;tk*DwfvfX^oX1n?Rp6%v)I@`U^zh=Al`DC`6@6XxJvyXlM!=UmqJo(yLd*7m^+=Mop+3}*&w`&5SWMHk-;XFJw* zO@{Mr7vBu$H7>px&QDx?Go07C_+~f{y7*=|N3piwWjIf{_+~h#x%g%{-Isf&^M2OW zl<7=ieI1$3$6b6goefyuv`psF=D^&Ot+{Mp4f(|L=FZ>H0=&G_MYqUXAI#Sv;&uR;L;9=+=272!CW^# z_Kc$Z1v!8EV@P1PCS5~jV={w3&s^krrQ*&jf73yy`bx|F3hf(*o53K@4Uawbmc6>3 zoR23>t|vJx&LBBj{-jZ5E>2AmVVrWh zE!wqf z)zTY=b}cTy+=XFaH<0XsJuI70h{e5f`}XS!Xm0n|F(W+wE}ioFc|+XFjdLHK@HSM05u4joo(BgmdN47SB>Z6WA$X z#i%6r^;$)vsmD$`EuiuDO85n!w0&RC?72C=o}Ga>hzGf*HLA`WkrH-zu(~Bss8e6Qh~^ z2J^{n_SEsjqPri@?%TCDv0c!*%3;C=1I|W0&l?nn7qhh(o*TO7g)e^rOjX=bzdv#6 zb8s#+M5)k=m$<8I=*fd=t~awh-RQaGtikNk;qRAt-Yg=`QzN4@j#@{`$ld0mLp)b!HOOAP43 z<2AM$65N+mO7NEyD!7@L$Y5ksT|;J{zn;3{5VN86yg9+IC-uii;n&kwopcZr>@ z0lgK($lNk}XEPUxIM=^4X!Pe#k7#PHc9DG93Iw zXQ#;0D}S`{J1$Y;Xci~>*!2^)45z!lw|jfw|F99%jfZu<@lV@C4+5fY)uvwUKCKRE zG_9Cht1JHp6y+V-jlcX|Z+<7|Z@B&)wfq@b`9H(-UFHU$e6!WZ{kt1m5!C&gzbNIJ z6<`F|^(1KP@lO)$9JP3!Jjwp8hQ0H}&S=(*oqwsG`+BPESsHumFSr{kr^2=Td}IHP zBe&U2MsB+=?Zy2Ljd9(G;utg=a&RMh915fSMs$y}7r(=~Lm~Xk$;$1)`t#elYYvlN zmi&!ownJK=hrL!{Kn4x96$f}Gw$bg z*#5J@{XE}ui#_k>yb|5%=l#5nRmIPa9UIE}uBZGmKd<9?Df72{*IV(je2&NSZvXjw zUWY$1;eEN^^L*%hKIiNC_uun=p3iT{ybjOj58>R;@pv9d|5nHK1WJaV*LOXB%ly2) z=XuEA>eNIo8@w;icfHJ#`8gi^Bd2TdeBL)z#a~C^=k?PReqN`p!q4+vPp1*%eL0@` zihN$Dfr>v};pg=;6n7Uv%=5uWGVa{kEG)7qVV(j z*$O|elcVtS{H_W=$K!eef*9w^>s+JA=l<>rKd;|I;ph216@H%IOX25uaut4Fr?-lq zKiA^;dHudB`FRRI&+n)3b3E57{2Whz75@N*pVuF#@bfx@6n>t6ox;!Y3|9Dg9nVXh z^T(0b8LG(Vb*@+V2Yl>)#*(k_^ZGZa_zM(%-uFg@pVt|t@bmmag`eXrQuukD;VS+S z3O}zuQsL)yiWPpIf0M$`@r+XVd7Tm!KmRQf<@q^AC4a2K&+&{?_&FYro5t}cirjHY zU)q67J8)?SF71HO9oUBl)Sn?6a6{JJPt+O@qyulkZ+b%8@!%%mUnJ&Zz7h`z2ks%d z^Jb#9c<>pEH>a}hCEA6YR(O!jhRlF{$2y|+tB4lCeh#wbd7|ymMed0ej|hqSJU_9gKPU*F-DU6HR{$Z)EQR9?YwyXlNB4@EUC( zdKvKo*v+=%*YaSudK(;!8nQSvRQf0CBDUu!{!Q{S4W+z6bl9SysfUU3FrNo~Km6VQ zOSByJ0oXgByC-RA`a!(O{3iD8Gon#Ph*oVu48CQU-}3>{Lx^pttf5c8B>DvN+TUYe ztQx9ZMMLZG&d}nmLhTiI{p;KctH0mY|eNn2R zA0V5%X~Zu7K<@X(=Ci+(F>3ucfw-X?iW4{7XY? zp^v6&Y5Q;B@VSOs{i31j2CT2FrQyxARA~=*TD9~a%&C!<(hmUlPdLxupMl@@pM`mq zT{u6QYbgtQ4D?-)4{B;@A?!CX-wxS`-w8beTfZCoZPrq&7Frr-*V0hv5=e~Cj~4&DPT5Jov8B(ogVj>!PJ9Ewxl1 z(q)d8dO;7lO-pNrYN_%dEj&UiFsBjI78nGxeLDw`YiMU_%>nw*Z>@t+qHDrJS}D9;TU|crQ0Bl4`?ZM zGx}n_{zEMthOWC_OD``)UD$h}Z-6}uGU*4%gXs0NmIizc9&5GqKJ1x0we$yKqaM}L zhJS16#7kPb`d?Za1gZUvmS#XNIjW_B&w$I@$cN9k3jJW`Zqw3E*wQgAty_oV2JQSW z`pwtUzaZO@H}hHS^9Ne`8L=TNurK?y)aXa7fz0_*O9!^#d^&`^UZ$nz;T!jgmfnLN z`MH+#jdYaJPDj#D;Azv*ojDN9$7238^saNrg)T|c(eKbHUGduujdgTPqoYl6Ix090 zUXc4Bd8e>XSL*0URUHkftD^((m8IyYMmHU`hU7s8K*mGzYwKw43G97ZUm4S&Ht zRn}3Dt8{p9(9r@bKI)R7qq6&Tbna;#<=%-p3w1PNBK-L}>Ts`)Zg~dwRP=$pvWJdV zLpDQ>T&JVj#mFD5qsw|Cj=2Oq>RBC)hMo)k1mwhg9i6&MM^zRef3l92&ec(!WjbmG zksvofDnFs4MtM5AyBB)EUJLy?WaF(m+6fu4R7cg8>u4JE>M|W2zC%YPu=ftvQSWg& zdNNl>yQcu}1RWiGL`OOQ2L9gQ52*`TJ3>c&q3i9_(emejZG(=!flmJvdC%*}0on0_ zj(Y0!G#K;6@K1(rwMs`Tpoc(z1brOwznC1>QR&aX{iTlj;kROjLY$c2u|h}pLuzl* zQA^0_E#P%XNBa@m`KFH2R-)gB;0;{>838%-F^&Ut-cLIE#Gt2dAU{K99MI9H$gTAs z@O%-+b1(3|rK2&hEnn;C1k6FJq^eFW=L)(>QvX0F$wwg^pxKLzad{=Pg~$0HB?WT zH|nV^M33KkY6O{HSx@s2uiQ*ei{tgQ3bLfFp1y!S2FdNLr<4ReHG$+nrZ+&p-g;U% zOizzM_9DLeT0O08r>8@RudJ!3c(b0G7wYM9=nt;e(@Dq-*ws5?{grweX4liKT6#KK zRZorI)Kk~n_0**ZbzjrdB7ku}n|tuj^@dDQX~3H(XC?8})S0t$ONlho1Ul z-e#7bW|zT-SnnyY=jiEe%#Xu&@1q3NJ(WJFr|u*4G!QZizJEhjLf(c< zUkg0Yry%yX^|bLhJ+*`G136Qorwtq6e_l^d!CnX10QnMfa3t11rab_>^YyfBhMu|= zqX*;we0M?*o~EZy6AiTPAo_fXHJ@Q`HUSG{+K+lFf?f+<{d+yVQNut>f6>$D&__N7 zzRz)NY8t4lih-`&tfx+p-jFiLwsSa-KSVwJ+R(C-z=OOR|HJ;*GEmApI5!~8v<8|F z-&(VQ?tuRx$b9(p)eJQCee5%GN{{L(uC{@;?$*;|nA?9v{S$iHx(_wB>FM5g^)%uO z@P=;u70z#HL#lxiw!j9bbm(1w=xKd*1D(n=(0`{G=mSXe2?nY+$UtXC8K?(z^Ew7v z+txs{yBlcYEe5JP%s`JrFNbu@G0<-4&yb^;YM>6+87Lk4DoB?!0~JH>Y-FHGm_LJg zDPtp-}%-ax~l?||fYLBC$8mxp~SGmtIUKw047xD9&@83w5`6M2h~w;c6h zzYF;QqT3Cgh$T%o(0=H4(1q|#e$+sBLC<*Kfamqtmv;>GIc)nn1N{!YXNiI4E;LXn z>^5&0=qz%^e_^28A&VfZAjh9K(0elsbPV&e&@1o4@mz#`dCfq5zckRa6$UE0-9WcM zR?NlzLcazX@rr@|K)lE2I1Vcf)MyX5zi6Oqq5I!qpfQlHOAWN~2?HHOe88&)T95eQ zpA6JuFX~o`qXV`$nv@<#cffx3ia6Q?`51B(vbb>^Wu+kg9r(76qijfzeb`UPNXR*D z9IgJ{KntqH(HZ3J&xoVsKkzU3h&jG8(DZs((iqt%#T90TI29}z&L6ES$5h$ z+YE8k`v5pYjB#-^61-j>8AprmjU&UAab$-K=pRRCua2X!U4aMoU66&4XCQAv))vRn zqaEW&zaWkrkV<#Q(eC@eYfKy!!XDK=j^;r>4>?#AM{V+e2fm|_okQYi#2se^qcK;-(Mil# z{~PgJ;^;Hz6N9kF&?yV!=&_sQC=)!chO{3YM{7>RQTJ_eH1~PzHRh)v=0(Vbw1D*d zIga{5PlTQWc?dFjOB@Y35=W^|#nIER#Zi?_*q0yUsO2wl)Dbcg_D0xCK1M$5C6MEo zuYVZl2gLj;j>pnCvON+Mjs|{%{px0saJJm?i**MyhWTef%#nF7sjlGRj4U$_2 z@rFj~jd|NvM!NBI9NhwW1oj@pYHP3t_AbO8ZHFFxj5Mx?k*FIPMeTCS5X!GxJbh@gMme(@UMmzdxjg(W(NGno|v;eVW9o9RHw70Jj zkN-yMywXUEA2w3nJR{A(d~g};CtyEhq{@#NsSzZ1xsm42LLc}RO+mf?7%3l;Gt)@7 zKre=D8fT=n{jhIO8_9CNkE}yw*tfLY6^Zf_#X) zc_WQfX$JB~BOmjtRsjQKIAq#*BkjmH(iX&*7s8%oq?v=kp}&!ItBsU60Qzq5f;LSy zQh(?y=vhwz5Bv-9FED?^oA6x3M73Wu(xmUfb0auluKU(V)1ena@--&fbPjuoyln?? zJU%i~6|;#J>_VL%jMVYCkxF4#K85u=jWiaLhk51SabCcG65_=CG7I{wF;X`4jgU!i zV{f4!+m3#37^&YnBXvED^W}t*>V9RU^c~peL=&}y%>M)D6Lj~3Mw$)13Gx3xcm2&s zb3QZD{+Eq(9HMyz_#nE^jWpt8BlX1m#Stdj-PT0Ss+j0YtBJNWgMF2W@^eho*k+rXvx(k`VIPLlrh~z zLmmY#$eI%Pmw?wTCVC5Y%DW~S`L>Cgj5bj#NaYzOs{V$Frolh%dGLPHL>n;w1Y&>I zM0c(-k$H@X>TfYoQ|OoHW1o=I>~<5SzGA5_XQG~v`S9HeJ@r2(`VaJK z$Y#X9gFdkywd=-{^u7siK$_?d%)9Ty8rYk59A<20aJT9P@j3{i*Tv80_Vck^4>b z!I!|o;-8zSQiFJ!4xN0?L|riN^C8Z!gm}6a^Ln;;dK~s@NZm^Dw6JD8{RRIVO+3}D z8c$cJ$J1H(Rv}gj`+nGizcx|xQzrUzMm(-(x_762g!rzhM-RCc)ADkryy$~ z`)&vBUh%ZOTRdHMM?C3=$J3S2H3r7hyn=WtEQ_c4%pMv~JG;cw7TDjwUfeXETK0o~ zW;|s;Y7dI1H7&twL_FON`vJ)Fkk=qprpM#;3+j}_Q~aIr)BuuyZ9L^b-vBX91!w5q zJ+N1e;;HNCczPFl-qrDR8d}pBYwn7tM$kEsUXX?V1(vVk>D(LfbmGZ)I)eD_mx1GM z?8lq&lnuRgMLbQ}A5Swsj;AM}&GWF|55xXEo?d<>o_;}2%7J*QJS(0$K=0lfPesr( zArC-~ei%=4UyY}?5I?;%o~Et`kB?A)FOJXG@ig!y?DKo^Gy}Tq-#Et5#~#G`=YS7# z5IM&nOLpUY*?>6Y*rULEe>`nog5&T^JZ)TqeftI&z-8N~zz<%(!(IqpnFrxNg1$`? z=z$IiWRFjvoXQE*7Wy#kKOv_OGath7I2%vN__fU~&_iLbvL?{UA8{U^j;BYH6DYq* z0-d-jfj+I1KuK38P)WN48rLX+>ZK*nR80ci_$~IoQUc9~UIKXyvKM*v8Yj?5a{_I= zB7v%YhhqS#ZcLzD=#%iTwI|T*x(W1p#{_y6zMYUmkUt?aED2QWFnCoa zLAw7DPY*-4OiZBkpJD%n>uX#Bb$=v*`a;GHO`rj==VLzh#sqp1dccGPs(woXHG}9L zO`tSLW60H=6DSL^Yfu7>nhu`B5~%LT1bP#(MK@!gpnpL8?Jpb=veC>y#rWD0!gvl3`2^y!tzgRXWafo}K~_@M_vI_w3G_s|Do zUJIV@B+w`DABL>^2%LWh&-byPOVIB)*1#Tu`EAg1ARSI5(1G392h6{P9E0@!HGwK0 z15ZfLKT!X00+qdyKu6w1d|d*)wGI0O|MIVJeEy5`Y^G)@X4;4NaY$RqOnVV;X)#mw0Mx6C{67d)l8@1Yt-FL&Ov4x1UcQ(OjDpYv^A5xftl`XW~LR8Rds-;otfgUGt;OXGmTsW zoKt~!n3*kCkS+@>Mf+g&c$b ztrxKd^Y)9)^xRN0>Hck|wR4a^%1pKr^c#VFgG`0L|6269-b@4T#~RF^fy~GJL+B%r zL%VT&_F-=v7P4Mo}20)L3 zJO=+$@Z*^+y^nb_=-;6SK({@Fe1nB@lPxq9VoI>k&97s>&!O%n98cI5jfG~yK8N_a z56!d^^S2;f@fRAdhcx>L`EQwN-g+D_$STMT_!dBZ#eDx>;5llhh3}wViiH|uKJ0fh zJ%D-jZ_M=57z-V_0e+{2PDmDNco}dNSg2^Mg=RozLD~=j1Ei|H^g_a}sENtf>3mtB0q4n7oIt#mQD+?XMym}Q24e4y5HFYdB z4D&IN{nuOQ$(t>-7Lw82LN)TS?^zc512IQc3r$P2&{9a7t`?eo1=jbs&?dzHGu}d5 znIPu~^j{E5sfFr6O8Z)9A-IjgT$*g5m!an@u+ZwKEOhF23k|##yyt;8e0@H&P%&h~ zR`6VIp~9uW2mAOF7TO2>3ncC%{Lv$bbFqaM%t61+z=e75mn`%U{EHzUz;6Dig+82M zp|2pnKu$gl?i0cL84Er3zJ=z!V4)J&T^CyDerWpx7Am{fLW^LZ+>Yb@81@~$)JYa< z0y!}i`v_n4*Kn@fhrQilp*J8qAU1IM723E7Jm17|YnVurJ_b+7i7&A~&~HM%h5vWR z+RrVN*(i}xKe132^dQJ!TO!Sb-gFN8h55Ufe+GFA^Z!9lK4YQ!jT7;avP9|*J^6p& zVNRsp+C*x#8|Up&3w1Ci(tA6>8S*`3NJb(xKY{&nB+^#IhBm?adWrP1C6P>jV4o4o zGbPf(!#JNl#c`{hNR^u=(ty8k{z0$$3g;5^t&oiRiS!)wM##e7ke`}J^{z^!McIke zp;;oGyfKl|rzg^$35m4goB`xObk*gF)D?0)B&!Q}L6*axUzSK;LjMXe%}J!kU}r*C28X`TdxwKF z^uHk6h9*+Kmv9WeO{7}iB~l&8q<0c24|+fR6QLi1?166;^xF{QxhJlStzpNTegrB+@y^fp-(}`USjpB+@?k&pO?Ag1?R&u9Ggvvl>2=m4TMa9%!VYtnn*7~Z-jgVnfVF! z4LaqAL|TdYu=S|_Rw8YmYNeO5tkku!l|F)|6e}IN!brrB}0(xxJOf z!@dXdBxEq`ccFJcHfLCAD{@xCN10Y?dJMSg!TuHdRozNcVOL4D(qnC`bkJm_cVOSs z!ActvtyC);`H-zmt<>Wzj@gem9?ekSYNbaoe;V>>Jk}xScgQAu+-u8KR=PaLN*&u- zsXz27oTj_hquaJJ=mD$xwg-=>3W3ZKGU|x5Om1Y%NY1@3% zhW{5x!c$h7e1nyaBc~hYMG(`&R{Ef?m3rS~rFyqo@g9?v-hwpGv(k^yhHJnJ{szz; zAyr0M=|7U`aLT`a={n<(-Us~zpK`Yh4r+gYh_O7r}%g@ngwUx#!v(g=q-mhEf z>0hkW{X;8FTZ!Yl8OM7oc)kk!8vIG3D1b9wjKVdI~wE6+Z7`n$^D}4w3J4ExG zl~T1f>HxVGQhfvV8v0Jiskf}uejoM;^VQ#3snR##z1>PSosAkmK79fE2E7+K_CIhA zV*WU!%{4aC4Yg4vNCQYtl8v@y+GrL0rMWgbRNY2%^fp@D$wnU`z8lhZkd1zVFZFsG z)i&6u6Lb!=y^4*-K;M&Wqj}IzL0*G=4A~1=b1l~7+h~0|8(jmPdWDS!WZCF$%xhn1 zqb1O3i9h>pSsy7qlt|^gYO`u5c5g} zHmVC*g1Hg+r`EI4vXwR(_#*ahmW_5HW*lduxEpPB{0J2G^%!C}e#YS@xdja$JA@$bTXvIVuUccC= z+8Z`{dLcNiu~Ek-ZBzn#Gvbq1*{Inf@PoY;{x1u0oH0+G3l62=^(J^jt|@{&8F(JI z(FVv8_{#3adicgZY@@$mr)&ggh~8$WanR=u+2{#oSF+RYJ>b2|MnAw_i};F@HfmDU zPCGPqD*FM)^EhgJYs0_o;oO6?Jq3*K*ytp34*w6w6LxAfJIyrO@qVhEhCo(g?)=6^ zk70h#pEi0Adf2b1p|jIpnAfgur=^E+enIzv+z1)=myPa&9(B-08=-GVvePc;<*?`I z?PRZEr>B3jQ8x74v%mm76EYvN60!ku=tt}m^r`N4YF-n)O6+t!^nx5ab-mh7&rGz_ zYmme3?DQ@4uaMJ-AL?PJ>a|cO%TAr3uZ4_=*2myT(p6rrN3BGj^H^ z8TP52ra(qKZ>Oz@J&xGhuwPzhr;PuCH*EbhI}Lc&PJ)2z636#dI~{x-{XYS| zhwOA5e+;AhF7(HI;CtAw+w61_u@~Tb8}bpPaH*$t&?lIG4Y3;? zWNYo9Dv){u9CWm&gU$_h(8I8ew>qeOF9+?%JUP!n17O#m=b&bgabq2HC*;gz)P=nR zu>rHNzQ92r!v3<2gY*wNXxlXoT09hcg?S!iDC9QCfm{bY0{!R&2j$-aESR5$oP!)6 z;h?7fa?n+f{*c^>4jKljJR19auY)$iw-fR?WcQs8+KZTOk^{e2<)G~O4jKfx3DOU~ z<%JIXyM%*iu7fH;zJWhywS!tNa8MV>rpK}FO$V)q*j8fyA-kS(&_{^nevdu)$wAd2 zmqR2->SokEfb-!I>=EoWkadvVknbRM-*S-SIQA2=|62!jg}xp#2{QL-tbyM068b`K z{~EkDVBd}+Z=ZwGP5>AF;LN1A9W)sFIAU|*oAj=ORzPoo&<^a=GuVf(95m!f90y4D z&kkC2%t1Fm4`1Yy$L{BeAkK2-H@wg;9iFw>}Nwj`(5*>$q73Q<< zNumPiIkzOy%tz5@dJ-Llul6%Z^cDPvdn11s_&`qHoJ3ph#2!5l&XAqRnFHPZN#LB3 zL~mohY&>w^hx!jBk!~*b?Q!hWq9j`NEO@R+qBcd?*D*=-9OnOp47fXqs?ScM+#!hH zg!tGb{Cj;8V!3c2A~Ny^|?(WHMa|+0z+* z*rhfc57;BGPNsUR@CobxVjX13W68AZ`DE&Y`7p>7Na3@|H1NJ;da@Y%wK|#h!}lwM zHYL-HxroEJ96IZ!WV#MA8uAcgbC)I4+t7y=B-7SclIb`kc?$OJ^<=6uDw!6SCDW!m zz-u1%^%3O5S2`z|(r03yFy9DY@;%t6<=E%{BvadW&|_^f4Tm08l1z6)uR*-cl4N=p z^JZ^@Yu!rJ@6KdeHyy`i9b)g|_&xxh_X2N#LxW%2^*ze}F2x0TaI?t}|M@-=*ZL4s`Id0a@uUl;c_E?iok;qjL{{x1m{lzI{K{Y#malY`2yprNcIA_RR+gvpncDfKEXVW9wK7A+ zE??l9sbqO^D;5TPDF3!D(3M^m2Dmijm0lOZ>18_<$ZQR@`Z-?1?Aogv*lKKj>=4)e8F5LU*^6bz1)yx zs7=e0Wiz$p3;eQt&-8+@599AEeP1i*qh1!Ck9q0G(%o*M3w^ZYeN-$#Fyii@dgSUJ<+_fk9a_6gv`op@t)rUTU>M^@oDN+Ztc}z2(b&^0mw_ zw?)q7J%gDCiosXSKx>G|^M%7#EYRM{8iB48ou|YRj4c{Vr0NC3NtqidOrq;5ajV&^ zudkSBT%zi!VP<|gCsF;PS;rSQc`TZkBGF6EMQt95zR|9!#?7BU+Bk#h=MQ@@@nHH! zD>oSYzH%8}7LOM0VEg#O9!xCQzR}DNhKDjYnmEJhrNkYI9Zvs9uMLHd+L}mX4Yj@+ z-f;Y(Vu|FsaQLXLjU?VsYOCQCs zN?l(#BM}P)qlh(;;G$Mj1l~|;sNso}KNO52)OT$D-!;2aEe$P2~OT> ziohCf4R5$a`@+R2YQ1P)@_M2$il`&UC2B4Lm*^V*WNz}k62+6%k=KpZJdnO3IR?^C zBzGltwc5(OaBa0dk?XC-#Lvbjs=em%0uaNDg#MrsU1m8k03Y4{87m{z6*XWzBc z517^xX zB=vG7oXP1_OevUNMz+}pCsIcw)Sw18O}dW;yNYEN6#kp;LxpN|w)*=|(@s~hRo%}# zLpNht8r{)i(F^!q5qxhP^>5L2E5*55eDKPq_lSK1a1|Q*^?~=O1k+k%*;nuxZ9+m# z`to#xeS~iyL){l{YxFhY%kYr9Y+&s(G{NOvcGt>3$O&&Hs^5GDzL)O*({LSs&u5^( z+kFJtx#=42%{>40wC*$He6|xgmGIY2J;QzM@F&RK!+j3LbJM-;_`}2Q;Xb-%Zo1Dh z3pd?|Hj$g|6Ayn3&OO|x&iPOOxKAHDkGc;k{xXewxX)DlVH5Xop9tsw288<*sl=o1 zvzBdwYq(F>%ed)2%PMozed1T)ru#In%1!qFsky_hz`(jr({uF=Z=~$?hefADlm3!dcA5)*F zW9JA1$3IWUj)mNIpCuW`2URWKmfN3obNSe<$l?^TEsY3UsKXzZw6Lwr?^RSbtZ;ZM zsd#j8A>r#1&hIa7cdqem+X)$-DZX5DN?Jj7SE)0NJ%4Tz9Vkl7*POp{^<~AQi;jC% zX!QIN)=aVBJKfmO==pW5nf$P4nMTj+8A=MK6piO!EUwY>y5`bx1!ctrCHdovhU2f8 zc`ET+fU&r6a{kx}WlqlprP1@dfqryB>8SahOGl&UcLhUP>Db~D{#iYZp5GqyrDMiU z>iP(;&u?jKpCDW5$k~Qd%~4e8GsK8u+?ZHZ*#E6Nwv{Us5op zuzoxD*{RX<+e!S${DO(DSj|y9#_ui0nryu7Lew#7gJjE6HdIyj23)^^e{`lgF zMdSDA+{aF%=QkhYNO$C0d{?3y9KR1GxG=buZ8Pv7zbhq{7L>V27Z!~x8~IsVcijCX z=1>$r@OescgNXW*7(tg_EPcY4U2p6J;&&u=g~)C9qsyS~K0{MzI;zaF{GuRm_{%Z}UpQsXwiz_`sXD{k|PiQD`V z;x@l(xXrH;ZWoMlUp3rb`N@TLLwt>=d~E&nLi_sW&Fa7YfczNLjCWu7rsnQ-G4()K z1DNEeQB8$i3wG;DgO|z)Id5_kIZr6qhp1iOA6p_XnD|_n1&{o==OcN+lrO?8IG`;3 zR(A>4&u~wwDUI6qWcci^tK^!I?$TnPC=k8aQSbFNNesTQzTgcEzR?wpljC4oG!}Vn z^HKW zZnXI+`-$KXXbolWXxo9}3^y;@yu9@bmqQ@yz4fgKUm#dR&8Y}JYCS{c;A@>)uZr~h z!W&GiA~^)oI~Xqhas%lnI)`EMH~iraHtw&V$hZ=lT5V-sxVBoKNcfdl)p{uNMA&M* zMEm_=W&Ql+gc}#lNm(yke4^GXaYe!wg*DVVk>I4P87ekW>y&sz+oEuWQ%5u>Z}q~# zCu*%XT%vuVu!d7dG$(KM!oeqEtv5`O@QJ`GvPL90vYH|>il`L{o+5CGuHlQPvX`%1 zWlXvDVzZTexGm?wd~*9@opWwnU#vVT5_hcZ9mm16=>3q_R?bED3Zz~nFeqz=Vh4gf z*c>$+!Pa5u~J;bTlzVqL)8@ zEdEazf$;Q}6U*Ga;SJRni#$W+qFl$gF<-0>CEif&SmnoXg~}6M;RL8oY0`bubu3_@CAZ3)SQaoqt-K24!+i@^{Pm}FTBCTDw0DWy@TQ6FE@~W zqH`D~f5RW{VB`M!iHs|;snu5Ig=?$ziG*K?Rjr3IPlT=3OSIn~R@Tp7PPlQ=oRsy# z#V2aL5?3T_QCLH*6A4brnxSG7wN8mgv@Hr}ICVsG@>VY#e4^HR!zJ1$3TrrZM04_1 zFC2U#)_TJf37-h8B5Oo~BdaMAqlj9O;3)!^=o-FwDtr0LRmPNSFE(4rhud-<%qO=m z);Z_K^~K7gB5}vc-fN zxV+`dee$);AE_bl8O%ITJiclMT0=yhFC4yNf%aC`2y~t3JSC1`Y|&UERWBG$%G^j{ z5?xn`Tg_&DeZ@rM5>-zPGxM{-7|aU3Ez`KrPDRJ8VmQADhw`SJcDxTw|OJz_ztVHD+$1&$npC{BUa;eDg6 z1H~sIFWTIAKM~l2sloe3TL*(tL~gYCDf@}w5NHi$?`YeB;tV%0+Pu8=3ztJ6>%H}@ z2wxyrL(QoOK59Kf<=|_bTCa-q`@$PctRguC(mNO~{&EB9Cpw2=@;Ch94mR$upUAip zn_6vUUbwbepGf$XSk-zc^F-Kcy+r%{VP*aN<%Am-%}H4=TzsO|D{)1_7KJs`I+5U{ ztQjgcQR|d=MBAcphEqp0CvWw_!6$01H(a89qOgWjM>HpI^}@j?Vy!n!k?@JYDzZi- zII@}|F^Z@a37#TwiLT*`r?Qu?TxCqT_F}V@e7G&=!F+Q2Vx4ntT*n}f+^$kzZQ>z~ zrWFIJ)oC;u585j{`bv*h^Xn=RLXOnhq<3YeQe_iWo{qU8fkku;U4C%4+gZY2#adTh zAJ* zbInXffVH+b*<43v6l3MGi%UxS7nDpW@=AhL zE+`$9T3k3ef9!;^)Y5SUWyJ+0`QwX*TS^!s9ofjvKuFzscIlPZEw@*Gk8XMWs47;o z&8iFmGxA1>fpONr4~#U3+^(yz>#-zq9=NxV+`dee$);AE_bl8O%ITJiclMT0=yhFC4yN zf%aC`2y~t3JSC1`Y|&UERWBG$%G^j{5?xn`Tg_&DeZ@rM5>-zPGxM{-7|aU3Ez`KrPDRJ8VmQADhw`SJcD zxTw|OJz_ztVHD+$1&$npC{BUa;eDg61H~sIFWTIAKM~l2sloe3TL*(tL~gYCDf@}w z5NHi$?`YeB;tV%0+Pu8=3ztJ6>%H}@2wxyrL(QoOK59Kf<=|_bTCa-q`@$PctRguC z(mNO~{&EB9Cpw2=@;Ch94mR$upUAipn_6vUUbwbepGf$XSk-zc^F-Kcy+r%{VP*aN z<%Am-%}H4=TzsO|D{)1_7KJs`I+5U{tQjgcQR|d=MBAcphEqp0CvWw_!6$01H(a89 zqOgWjM>HpI^}@j?Vy!n!k?@JYDzZi-II@}|F^Z@a37#TwiLT*`r?Qu?TxCqT_F}V@ ze7G&=!F+Q2Vx4ntT*n}f=zs2`_f3~R_aXXo9~n5q}lzJ-cUa<3L?yKj9?)kZV*i-k!{GEd%uIRe~$HBB{ zEb`jQx#(Vj)QbcLWzA6RK(Gg!qlP2cIxJuG2nB<;HNJ3p%a{A)YneY%L*6r(d7yZF z)eN+Th&*37e8mFot*jB~I?;Jb9KqP4u|%p~Fr1XRk-{Xpt`fJJ&HDO^iN+lioYi`AjT8>$_v z{1~oKImPmEQ|@;-9Qj&h%~-NIMiDxec*(Jd;HIo0uOBOOB}Nf;tZ?HvL~!y|gZHUu z?F*xbSVi;W{Y7w5tHFE3f>y&Q${!0HIR;Uj0p= z2BV1FX!BF{6Tust}NK(L0IQxSaBdWOou*E+Rc z73uedH<(yOatNe%FkJlQ2GUP-4#VVc_`@A+++RPDaV0jj+RD6eZM8m;@GG&Z^-$)C zu+@5r_WQ%i`uWQVH!hl!vR=6OM6Fliii9l+Yp8W1!AV&&RBWQwDe;K5Md1vmj%ZHa z>V<<()LL)2MEgWx4X2K1PTuN;gHOa-Z|(x43mEhq?y8Ye{{vs zukjn%d0i!50&_&dp=;h`{Ox(`!<}Ly1z@`_pNd^=IUU(5WF8uFgO z%mc;at7f1zMCAFx;VTwsZ)J@@*NM(k;t0kTjU`g`g5jjhjT9!)b(Of)Y}VITOf)W0 z_0%vkKN~zQs$VqgaP0ncG%-b@mp^_i{!bWz@bs1w%iO);4b>NmJVWK8T*tUEU#t!# z-capW<;QS^$|;tQn{vOy;mFr2YsQkzF^bT!#7mAv1UF?3dHqXA%c^y z8oW72wjC(WaPy+g%Ui#2IRvuaTi=TC1%fryoQmM1)-zNN zzSgPrsz|>tyurjOl0zW9gW=*YH;{g!a~LLn!yoQoHe1Pu+j1VvC$}%wIp@Z84DyKnCtZHI^hp=tpLA)3GmL$_#970A zs$HS5Tf5xvuW0ih`FFSs*cHRy;nG(@Ax~lVgKcTVK9r*)D_Ni^zgJP&u)^W3q~g)V zg=9CeARDRV=1J7OZOM}{e`j%F#*d1=TW}mqi^d|at(=SQ6-d2EU{KZ!#SR2}usLct zf~~{yMUPN0cw6HOm$!VmPrjDIK6|nHwogqU$PgtJ$otub60DqUxz(W_~t!TvWek*5TOw>1bk#L@$5*Sp1(b z0^#W`CziQ;!yBqE7I}usMY)b~W4>4&O1z=kvC5C(3YAkVA2;QGhr^MtRo09pn`0EA zV~Lj>iwJJY8uI$FGFM_0VaEzLjza_|Up082iq^g`iilM-Ki*#i7quF^M=WSHjH3Lp zz>#AR#VOD_yl=F1p!h`OMVlM%CjxsgHF)1>>tHa7$c;8XWj_%d0MoZ;q0 zo0qqK;c^IMy|=y<;R^(7s5uqEN3Cb59DJ=)>s66{UwDIwRV0T%dI!VBUv41%MCUL} z{)Ru?!N&dd6B$=xQ>(4a3)fcb6A8Z(t6C3bo(Nm5muSB~tgN5EoN(i!IVtOfi%-;g zC9X)=qOgWqClZ{LHABTFYMl~~Xj>G{aO#NWlow_{ZGCOjB{^CAJ49&)Np-3L1&=9eNBhQVd=(FbyNu-aHv%N0f(%yW5*X3k0~fC zD!uSy4jPR{=PAxLGucN&*he75eD#Q>9jm4TBoo6wOa>+yO{#dBjV>r1m0DalIe+Yg zveeRX1!ctrCHcZX`En(?$v^qhUSW5D4Y+%6*zG>vq|vYs%aB%wycx z{JKgkdwA)%#MU0LTRX>IdVUntF0I554Dec9hwID3b(MMC?`v(OVjK&{z-@p10?Cut3?$!Q&tT)q{-I#t zHT?AsG|yih5%Ey5@O4}>EPQZW%;xi0(Bb0o#xLBOBKJj!PgL!S%X9czZgZ{X=kbcxq2TkjhWRR*i@*M%VB}nVX@7kyBCf<6EQV0_ zD%c*P^Eu~0>&7z2K(Gax6G&d5dnPiE<6@c*WqsyX`qb=Ta@A@?!|#o2AlP{h*U`W# zqKBNXeD19_k8_S(^IWCP{js7sKBn1_<8pi4K5sF#T)971=A4U?2g4^HO1$!X`COS7 z4LgwjYB|i$hA*7%jPjqpaw8oJ1S`X!971IJJ&5JGiwJ&jjNImYxaRST zQTyVcdkZ<_Cmw^&8{)cC0Rz2W8jf?4COPDS{F#mu=@1lDkRhJrhk zHQ`_swKmve%KC<~Miky~>V$$jlr`aC6tUKIfMh?EHD#d-9)Fd2v1)VtN_^bUG>^%B z@;Q%R?3#1(#)HMY`7il||6XzQKMC{PrBA{L|0IkP7YzI43jYq6P71p-Y@&PL`2Kn$^2|^I}+O6q5P-l9_5wYGjH`-#G5}7zEFF)(Yt@D;Zn{yE^qlfrmV;PYz-gY)<@c>#3k>?ad9n=sm*yV*J^pu z@^c(ai|Wa1imInpCsfR$@QJFU#!v1Kr&l;@83sP&cp^OywI^HW3mdPi%;SDvYaLnYYhJ^f4yLZR_2K%w z_4nrU*N1aeTdU^xS2xmejyu>s-uQ%CE7;zFst|VCEj2$gtAw`_7I)VIR{!dmN^E3Ezq1m@&es6k$D^!(|joF zGr!WOW(Sk2RwEjIZ(IYx&U3hq238S0c+x{joCVT$DT*KKW4MmFLUn%DiaUf%I3)VSYAz;dEz||MZm`=~y6G83yH0`U1h} zuU8;7Le25lTh1@k9`bem`pI)e&As)M^ODE8UsUgC*Wvh;+8YizZ*TEf_i>JLoc}~l zjGvO1(w6T*EYDp;@PlLIHs`}Nk6(=17Y8NZi;*A4tj1H#&)0d=d|$l9D%z*UN6qgI zFXtD`8gF$f!WS%N&b1=2hSM_?+@Y)q2cxL9!5&lAHmEauIB$tV2xwR5w&b~h!l4cBQ> z+Otnr5Sg006j>S(z6V4{R(uhPzWlpcq~g)Vg%^G+i#wlg>a2fXUhw$nFbj5ny(CUv z@QWp37OXeh z7(uknaN!O$dv^NbOV>J{n_RlLUD|{H59a`TsE7nlWnHDsdon*8+>V5HcPRfUx<`3s z_sm;;7V+kfgfG-yZuIV-YPi&LlJ8i zj>}s3xxAl?sDRIgBaa>%>V`_7r%e7iwwEP?g)1rFvnxg8d)d>}|D14&o zsPU8g!|4^yT84oSIi5(*L+#1d`NGEQD)YGC*V;(MI2Mk9+y43mk|(bjNWQ$qlE_~5vh&F8V8!^Pu`U$`|z?u!zisM;0Dk#pjjaq@>p zo*PI$-zR@{d7enkv2x982ZM#L<(k*LZGE^tZ~eXb{Pp2n)z+%{{nd?hoZ}9* zk2gM{)(W@1?+*@rP=N!4_xk{V+V?}d(OtT@!<@UIJ-ePLGa(}GMITs}lhEG0}c;)%> zxiT*rb|C%La+seDUpUD7@j+2?cj3Yr?@OVy)`{$$ltn%0d@B{wnig)#mt>__&{G z9+Ugza~{9gHRt4w2a9?0U-JFG`+e;>*JMhs&8aO_^!e~iLQ&Q`Z{LtLD(S{7(OB-d zua-2Uu&(rRLb`OVJyZH|SwrcwiA|+9ZA~hKcTVyzSuZD^%F4d%aMrwjcMIV=?9)_f zMR84OVB1UDPm zsndycsW>lF8g{B-MD{}4DMM;gH$!q#b?Npak7qqw@5Zcom3D{3`Gs2@(((EYq?cX; z*V}4IrOoR}``Xu)<|j3f?rl+D%3YTs70zfX{q{f;>4wtA(ig)sq`v*@NqwrNSNHTV8uSs#=>pJnP?N4joXE$OlN>Qb)}HKp!1*OkUJt}9)e zk}3UpBtyzsk|Dj5mML9Vt)4XGfm%}a9krz|w6&!=U#Ci2{?|b2b+EB?q{dxH;~@lZIo*KdRNxER%KcJ&%Tm1Ywm)qrN^JhYC0%Q`r*Cm($lX4t2 zq&>;$((iQ|O064bNFT&!O20o_Ut0Qn9jSD09jX3=49PO3v2^eMnn)k_Y${FrsG)Rq z!*riG)HpJKYs=6qo4vmD+I{t*6}n3{9%^N{w1l!N2NCUrhNeYtVN$|7{=B z_pHylc~5<*`>*NJgUj@XNEr>&q^eD8N$YW}tJY1E(ymLBo?2K>YO*a| zdVXiRbpPFH(pQ;vq{TFX;fQ!-|j=oG9TwGOJ{di4j|IFIb|9-D7{ob{P zv|>?pY0C0c>54%9mrEkBlEv*-Y?hi849J2*?b<@&5y z-_FYVb#i6NKDVCqo1>DHdq<+ws7s3U^*qRvHKZT1YD*^%q)IRKsV!~zw1)KNv(=kQSxYk}~S!STCw2{qS%t z>AFd2(iPR~ODQ;?Cx2Z_s&`pE>9Nb|N^ANwk;ZJvkOo+5OB$S0Uw%?YI=nM8;Ny71 zBb8Ni|1-3IR)Y!oS&P2N&l=rCFTG)^Dh<5aB5j^(k$!!rlJxTn)ubD5PnDi|Fa7`8 zI}flZlBbO?86->2Ify6*45zX)(^Y^df;l6gB8ZqVC(Me9h&d~UGbdEcu8KM5#Hol8 z6%i05$v3;+{y&cs^>9(to_pBYnwjpd`qkUrGlN{?fTc$GGW!wi3Q)oNyq;3`TVwr4 zX82IY5w9ED;WGz2R2|6cj*qY9Y8!m>rxS)`JL7Ime!h^m#pK zr+QV##%ojHVL}hc4&r~jG_n0`M?B={h|$(HFe+ISr!;y1e|e_Bs(OVmf3yV-y3UU) zd3@OvTRh&$9Cg=OU~oG-TzlOaA)+c8*i^wK8(h&V&ILysRlyF^tCpINlH*Jv1j42B z;m~{gacI)Q5Y?(!qMw!#Dx?`>;S>wJi4K@k-5%kI9bQef!)n z=w*d<&zqofiYXq*Fu~g`%+a!?8IB6D#f1H~*xz{;%$U^!2CGCtlF|!!IN~-;9ySRy z>vadew62i3JOr%Uc7?akm9e(96*it?hPo>)P*2qeAK93pLxBypbF;;wdk*-JA44bP zI^*-*PN?ARfQCyPzADZ;d3WI89|~C5+yUi!SHbWAd%Q4;AFq@waIYcXj~tD0O*2EB z$M<{lM|OB{tQ}S^vcp;z`EjmJ6`UXCgzJqRQP9T@(;k_lR&!IV9b$@6H7&5^6kgAa z6>g4H#dZfmpw`45a3ek(j4pSBggpyj+_k-MTI6Tp$C%-y zKwfX{jqu1-b8N0+hus=R!)p0(nBP1U?z{H@wJ~pDj1zyqlj4N+s@dYhp;oAEW`big z^zgd76&mq#>mYvqnpe#Z+h^HghL#<=ueB+2OrK(r&CiXIkDZXbJs{O46kcbEpywTX%(u3~h(F9RNl71@zc4^{n;pJ1;>X>>D!68m zBi4B8fcvoQIV&08(&R;wv1t3l^WbyI5g)X5z#UeWX!O(+cNCdnk6a5}y43_LXItQ$ znf!Rq&%;~VSfN&sHEw-vgM|i;_{RlT6y9^f+uN*=Wh~IEktxO;GQnl(#`yA%1s>1i zIjz^if)+YxT+IrNn_FR2OCt=)*T(IC7~$eXM@;JAh`rZaVsA&V$mjsd`yN6wO-BqkZi~w<*!Qu;jOL`j~c6 z4sX|wg6Vbpf}1c1uIhAvO)Ep8rrImG&(Gb?VaAw$r!tPKWP?W|oG@0+0UJ-xgzD83 z;ZpTCFqgk?Yr4u3>n9pv0iOp!p*6PQ=cYaWmUwTCIo=UipjMzcB3NR^bu+ZFwZV*A zCg|;?k9!i0(a_BX$KE!_T|Bo}YpSExblJow&F^u3?tgpN75n~ah@L$aaQ15f?hKd1 z`wz8Hd9xmFxTB9_CK}>!RXse{+62HjJ!6xYY z*a%&hnV>sogDrbi#c`IJct|G_-p2;Q3zKX($v;z^zs0{BdFF!sSJ|N6h{ylCH~qaC+~yN8qj(?1B5Sw z;D$nXnB@2pvUF|HzL?i@CtKWi!W=u_vcv(+tnlzko>?0kyfxk&y=R%>(c4xSG0X+; zD{lk!aV^3B@Dm92GskWnA*@%PRK=2&MKKjsD{!m?Vo;Nnm|t~2HsT+0T{PB@^#AI@0W%nsE% zI-u)JM@)X>gkB>|uwIcl<{aYldB_p#Y`+bIrnP|Qh4T0;hCgTHgsZLJK;_S@ zkHMoldT1DDfvIYac>85vaPBw{(pxFWWPJTNe;_0Vy#4%Ta32^${M#FO^M=yu!^L$9e~ueCa; z*qQIsWxQAY+H&vmBEi_GBRstv02_9OfLVbB7N%7}7pGd7d7huI`RDjooDr(6Hp3M$ z#yEkW6E4*;z%>)harr)Tyf)ty<818lF#qnaVUaB^S#O1hGz{_hMg!EyHNyaX{yERe z5OJ$E-kPqA26r^kbEF>5(=tFmQDqEt*TpyX2L7I3+YXhlnqY@6=D2sLHICZK&%foI(~s+S zf4m5?8kk{&r~G@LSaaN(XoV|W&9MDaYixDd2LDRt-!t;ho$iX}So0h|*B>##LpBz; zk~7EnIsEfdz*B+er$`i zQ><`RKO?Ny&KzfiTVc{Z{vK6mi){;yu}e)eT=16-4wE;-_}Y~*{$3?q`L+^{T%eCV zl?}04YjgA(pAQ1TD0uQbsNBcfk8>&hzW)+GPHr*7S^T}baF{u6;-6IxOtC@Zd}Hi< z$P^7COi+ERErz-C@4!b`VXB`#KIC~c?7%;R)HlV({QXnyamM&(hAD2lWQ}1v%+ac{ z4&o9OywOJ!GtTIu#$F3tKim=USNn>U;+rewEkf zq)O-=tAclfEz#P{0u!DYp_a%Ldz>)AlpuW!I;(;H{C)eDO2#;BsR8ce$KriKI=Jz% z7Mj1(!D`(!(N0AftHdecDqB??V4;ECpXs2sjwW{LrHs#`6|tI!3eFv=hIRS(O&0EU z-!0bVN3<|S{vzBPH4<*@2!OVN0N7nW2Ua+n;Whq#?$8W&!auh~1OnIMnNS1aM9S1LF%N*+^p z3vk!ve0X0}3{3_WLZn6!IHbv;avynIdtU*oIB8;N7YhuvbjE^!@*RJl?Y{@=x+++3 z(*(z*@$c^xta0u+{yjMwW99z3sAjK;_jDEU-sxh9nkv9eeg$x2a1m&%&x0b@*RUz! z8Pw{X1=k~sfLl-qG1nDvc#Ilu;NuEv_8vA~FM{J;1UO)t91d!ufLm(t`K(+F!*Ufd zCs7BNx2cRyv#oKTGXES}4)aXn3q^D_u}9Z~j-0Ch$gVHf_ns;8|f?(C?8eYYuN z=0s)mN#eOTehtYA3Yb1XfHOB|gJ(oO>~Eb3j$Y|7`L8Uvr!9xQ_ZC4FfdKPvtD=5i zWt_5BfG?irgRr0&d>wcVC#qqs6eS$uE{Bt<3UI>_MI1g>180oUKtW&r8Dfa+@)@77 z0xAua$K_-A_oV#zFW=V;Gr?@1unpGnCP|uPROsRmV(YO|-gJ37H4lm}$u0D-M_Kyu%xa&|cbTmaK>79y&6Q(N~jy-!89< zlOh!`b#o3(9GC+S-sM65gLKH=_yi6O%7o;%&p=Tz4>paE!-=!<;gxnKWW;5{`h%J9 z`dv1h9i9tuOY@-%AL|*-A}Ff)9%e?$;jJWP^b%-d<$2o34*9q5y1TUS&{S+Wqb0z#UR#oOPipR3GeBWu^OaZ5Ce*?4IJO!mexiHK*4-U6@3DX8Wf$rm8Kw;Ed zsJ0>>?#y@taqnKi4Vz3b-J1=Q?iav={<+X-=o=XR>^+$Czhl~;_8#tDd$klVx00b}xEbYvdnc1wqC1?kX#MGhR=TMRWW zy@Trf_&LX@2;}P)!@QY!5RLDkUYs2I_7q^xNO_#`z8H3mEdsfv%BVfM63(5hhR4{? z{~o_Q=8e+oG;!s}`#S~Dl%E6koGS)H&mu@$nGd&$-a)_Z`EbZW4*L%gV1uhFSgVg5 zI@FTGv`xxb-&_HsJQY!mFE>%S02-Xjf*Ogru*x?dIMVxY95hI7GWBf5Sj2)+jdly#1RUVaiE-E-i z@f}p&n+MGVZ(+MuKB$h)gX6WbU`^w+A0D4Lr%s#}=AKc-{yU4oiH~XdU&U}|Q4R$4 z&4#!RZ{gj#B6y*h59fSxA#h7En2XeK%XUqyGfNL&TWe!nm;&zK_Z|kt3GjJF7F>?a zf+KsM!CgTrxVL!#NA{#th&bO~sEh}%R>EZKN_fmz5s#(H;iaXypjA5)w6(I}`F{R> zTr&^M%(EdkI~#(>$>aE!idg8bj6J*OL6s-3Vaw^?#dM{4U}UV$d&H7fvvlP^JSWEu=INQJ^j&tc}%G&rjL3~IWjK?3(0 zinhFh6qP%$`|Jhi5`PJ172k&FZmFz zYT%I_@4@@dD{$=i8n*gp0J}B=s@NriR-GrXsQW9>IQ0w~UVQ>L`rL!+#dks5?FpYiuSY&P<2(hUuU>B^y*+-hljx3>bYY6WI3IFl%EbwD;!k znV!C?a50K=o8RFTtQ!6Z8rnaE&JMSrZ}Jrg8F&#?LoWZz-#FDb5OIdTr(O3JlKN-E zDVJQx-ku9tTQXq%i!@NH{t)iePX=SvikHulI{uP<(<~kACccK5j*s9}+xyV;Q8K*j ze&ZiZ9_rluhyA5GE9&{UvDY9=;R5s%T!aPHFIC8xK3<@~%uQUF)CdD5Dfjqw^ zg@!;?ye37rzI_LD@7X6fGGw4YkH65E{}C@R&^vfwFYAH*f+KqdM+6QC>0#Zo`_RC? zgCaE|_=PU`|Fi`<0)76r*Qc4MUn?Jfzd&D~R;>jld`TVt$B6$?<$vVmkWG}xHZ(kzX zv5n3Puf|CDcWWOCh$T6GdY6uJiUxllP#8F{$Nv(cDAt>J(-|}{vhRS9h~Qx%1GL(`$i1v9l~pRV90>MeR~dYx$$@U6vgTlZ`won?l2%CWN=7CU_@}Q zkW<&BSD1ddK%fyC5!|h3$N))esnH*D_V+oulL>(jtG@Wi$?;v}Lobk0mhKfa#2rG1 zA7{iv4L_&|l(fW06kH@jkK~vj>cg!Y^kL*ZD>PdA0jiF?9P+gZ1dZ}Q`0NGh#AQzTs|DNyBuPiz!lnBS!7 zP%J$i&BWLJr*uelBHfEC5H}~LE=dg!p|o-M-{E|692Qr}hxM&v@kp^l|7A{ulr9b_#m_o85b88Z{h&NO>3~FE8yQjd{O~? zFnp&O;jrcJn*zR8$tIn6r1rK8e^vQFSnwSQ8(jE6*y!(+eRsnAed52j*={AaA+MeG#7A%SFT+5$iJ>3GrV+Vs||xcYO?C zg!>(txAip~vtB8YP6biq;)W%}Jf*Qrp1pSL2_kM*nq;rQiC{)Nafn<%1}qsy&N?Un z9`**Kz68dn)P!6EYdC!JF1hs}oouw6M;zZ>C-+*fA@W^!lnaX}#a!~IM*$h}VlO#o z$-uQmLdexiEmK>aSB!$D)jC4c%(i6V#An>#=nf=CIGv2HvXD%>tON#XK9Hk67j9&E zLj8kHAT_BD=y=7FLXE8?VTUsG4S7I9tGy(3%o{^w^j?{87w5at00GS`D{mis4(e`pLzLv2BLRSUu|c);oWEV3tu(-%M_*l}P%zKHRz)DO`a`3Rip9 zBNAT47#d#Gf{o{WAR$N6cyEtgt>=8`S^yW?6Aw%-$0Tx7)6Qv{RFZ+o{&zx;mNb>~)k2JFQ1rcfQP3 zA9$XtW}ZxfUoR(Sm0J_ti}lEbZK)(JAeD5wpau&Z-JviplkB{^k=Pr~BR#o6U)0s- zVx4`-5IW2p4C{8ZgcAnppsb!qKvS2~LjQ5_MZ1xZYmi z~h$~OFM8Q=Po73yibv36PJZCLI=9M)|q+W6ucU={Z2v* ziGd=B1h3>6(%WM*vD`a_4C_9D)GvC$Ia%0o>vk{XMz+x=6EATj&DM?_NmVBs-HW(^ z-J;7J)2HH`F;^XyEqy>()p=x)+-!0}OP@Twp+mOZH6UFs&f<<=n8n>oi03+I1`<)7 zWn@p+6J&JlvBdTEPV#0`BJo$RNAf}+aew*L;!gMV>w6p8OsG>+v zH|PU)FIJLMr^3kamYSqjiXAt_zk;#c>3UlI?{t zq;qr(spdSJ{BiI!=M9+h_(uX^DmB){ZF?r+2xz`FFWm4g%s@SjZLi zwI%B{29wDbn}N4z9}Mjh0Lj8pWbD;KuI7Vv+b zqURjvdiYy&x0Vqu+pr(EO!F?+dXYC-rQMu(`kUFm_eps z01?%F&ZXYG%*}FN!7Z+9N80z_#7%Qu!A-t9i3G%HLf5DFiJg26GVZl1bR8G-O)*OK z+H2Ag0#3V#bJfY@`Ms;eb@O48I(j~_^NA;BYl6wPZcaq5RR}R(vyYpeAI_O3cP8FO zwy?=!98|wY@W{JA!K{Zhw3}9o6ed69&hI(FdCd$Ubt|_b79m!oQXefc{E#W>G`R^0 zujWlgzUWS_dt`Hwc?-$91Y2_Blp}Y`eKR+rizNxT=}(%hzf8Q6qCm~jKqm1?<6L{_ z1hK2F06J@p!T9_vVy^E-s%Hv0|JC8#vL2T>ySY8MK%d8)TkccNC%~2%xY@%R3wP*s zeF!v;5<%eQSg19qF>GN%$<|{N$=U>C61}1l+5Oyt*w#778OUXDixRJJSDf~8kEXoh zPP;jf2d_?$>j^K&{A&!^*I)~`>rgQ_?P+6j!h9h~)87Nxqx#7-Mro|~0=f~E6Z6Qp zQz>Lp;Ycz!bOmXbJe|k~MiXvK0_oq+fxI@E$R!ndllaB0Vd&ZpuxhF=%;Ppde#=d8 zr{hYfv_Fb8SgK2EDkO7Ck&Z*NCf+RF%9xU_=CUQ^2@&Wy|Q;M~LvMpie5s}^(M-sAV=$)#On zagR}8G0YhZ$JBy2-&Z76@gPa&3dzVd)!<%I3-Dh$3u=WAhDzHyfv!m{GBb7_xi;qr z*ZR#BE}}1gj~+gWJe?a$-W${>hwr%(kF~SO$T3YJI^r4e-_w-rFn!ED?fr%`AQMO+ zSq)uRo`ITX)yw7I)=G0K-p8C+4zbN$z`kc+GIn+{soLiXSvE)?CN(yN=~L=L?#8ZQ z_GbXeR|+GmRaK$L`Wi{kS0_3t7G(R5#ze=0e}B`|5E8W#iJSgra>&sVmb6lYBnTr0 z%c>JW+7@z9aGt#F9!)ycwj-7;V>pYLqueFYdtw@pM%-p^B28MvmrE``j`P8}UJy}x z2DEnxhQ?1^Amzaw@}`d(+#F*K9=6rt>X^mEYwQB9#XJ%#HFqUxq|Urt=%Hd z>p^RBI?4zxx{M$vI?X1nr#6-8{m#eniN7<&x1d$c1yGQcL*#O9kf5zW z>&AfLyUt)^Jd@OM9Yrqnc*upfna{;vuEnWkOylm()8tyn_2Tl6Uf6RN4Uy& zH*mJqFP8c&SkmXR+SM-JCG%G2asm9GM*T6!9ki0yLJPI)pj_=R)M%g!ng{Z@;pzjp zX1+r>2Q_c5qq!RAv1=oz)G>lwtrbeFHCe*M$U`+-RXE?^IGO0924PeEN%LFRxbc^Z zx&G_Na_=_;al;4Majs#jSj(=Rxy4@dIGvNrxPX>wTrK|oaDV5Sq{*ND<(kt^@v+pq z2>qJ|gSYh@(s_P7*C@3*dG^|fnD@{m^^*#?KSDIf${uP&e*8{Obfhh5cB2syGLFQo z3PWsjo|DPmN66J>{YdN|6G-CFZp65W90{Hi&b4|aVpWZ%aw2nGqS9qHFacn^Lk2fOv4XTkAh1$g8UJ5s@ zMH})||16mkZ39b#>|wxJBj_!^ns}$Ab7zlPksI@t6R(GsFvaBp(Vpo?+SnjjT_uPN zZlXyBdQ9e)&Ufb;I1guE7Fv?`$KtsgI=8r{Jr&XN^OEQFMNHR7nk-;Hn36o$67P(uARh@pE`4a)zm9vC2 zOb8?!_qvkkv+udM6mt^RZzfsp+lZtlx8N+#$a9akDsdOGcXRr0<%zmoGtz&^6>e0E z_S}9=9nxrQ1W^h8x_1Ph^1Cd@E*g8lnD^*Ov14O56~L^QdYdXYWy9l_vRVlOvT^+N@77N@X)~aAy$kHK zHUqB}@#Oa27;-;E0V*YpA~lXJ>}5MWc5(mHjQz_gOvCPqtoqVKMt9_NfG;)W|y*=En5X_ z=15~Uboyyl-LD!sU3lTU#5%t94YJ6sH*stjMFOu(B>YQ1viYz_}>wgMT=is z=HS^@B>(YqZq=rl+?>kQ$Y7=8@LQ*yp-%UV=TN z@YIe`5g0K;htCu;eWnWcYwI(Q&P-$?Zj5ANJ26bR3{Uof`edfn(m-bRl3C2zg`Ui^ zomH6trxVPEe)pN95%TOVIeE4qU=eE}s?4<<-i35K|Ds&tTr^k+135MR|DmKt>ubxj&K$wqPM2fCbUQFfSKOFq zImtrh{>>S!g0_se$!Ml_I53|6E!YM-LRl`cHzQwfHIourjg9CxO?Y;s4r6cQ##%Sk zWdHQI%G@4vokboT)_lvN}WUGDC{rfmNG!HnCLj!dh!28_9Tn6OK81tx5u3S(Jy2y^X@JhQu| z8B;XcjMV#V#Fjl0Dp6@EW<=Iu(Uu%ZYB&!C~)Iyi(_h7N` z($!*Nqjru=XV11w+vD?@tFz?T2?Y~bQ`^?;=qa_?79KBHmz&lc5j|wZeQ2=VepdKs z3w!!rdsh6<=zFYpVeRMDWpi6)vPs5KGL2E(Uh$r{?3o7VS>Hal*)B%btmcyp#$fpx z=H;iZ= z$@J*Yyx@i1d| z9CTJ#x5HtfwyQQX=Hy|aaafwLmWn(Rk(DjH*nnlU>d#}&n0IFm?47_2(3#1s%HG1H zt+>GC#`a)e)!NPO0Kva}pBD6#-TrI=t2MqRdr{b!z4S5SHv#BlGf6@1PMuuO! zrmQha%dOd_&dP7E#%xtoVhr-qg!v}X%zgbtrf$#8%**+0n5zkS%(dP+Y&Umf)@O$? zTPMwfT`|&z9dJROeR1I>;~%EQ&VK03IxAbSj~>5c7LKUHPO;Eo)lK|a@66ie?!PpS zFYQhGO=1G(Ml#IK6z0N4HTI2;JbSybDy!x;lRft5Tvn%PEZfp%3fs2hST?P08@7=} z1GdQv4|c;rdp2)CCDz`~mF>U7oz+>T!!mKUY~XfJ_L+hkd&Z?EYca=}ZN2V3^Cch3 z`@G4fv{W1$}qu6sb2e23W&136@&0(J=3}8(k6tj740$Htre(aOg(d^OPG3=S; zb6H#cBsQ?=S+FNYzTXv zowZ%WRv8h+4%||OP21?g-dnJV(LFVRIp{r%;nrI+=v{?5(OkPi#5uNA9d6%B1@3j& z5w@$s1a^w?Shm|fA=~nbEo=P5gFU0OlTr2yW{X~DFiU30vlC`FVh22{&33&wjCJ!j zXZvnn#Ju-*WRhA`W;FhCVCpsuW~$v_nZ8Uovogkq*&l0OA>v$PyqZ%!(VTnPyeU_8 z`4)CTlND?)`|51p1DBZIor0L>Der}ISQ942Z!i;5w2E<5S;U;abCMaWzmrL9J%rIT zeI#^p$`J0o-hqiZUX#(*Z^}H>v1DAvyE6+|WoBB}6TFAMhI?#d+B+{`6nCCu)@w9mHg=iKyu1>|tn1vE=@ZbEX%X~L z*stwr;nCfCOpwAAVeyuI!cmQhaD3Z)9&wIBkHbAfJ)-xn^DuW?_QT^W$zg+g25UEF z5?hd;&Up8|&&VGaum{thFurfym?5Kmh0Sg(5EiFR6K4Bw5JvUuDI8?6Luf7ND0Gat z<#GF3lE(;pk;j6>U z2oG!26t*;s@vuL1-sAR=0HK#*Z{hSVRfHXz?HA4)wAG_>#2}9ejqE*YT?_Rfea3s# zy?f20+3j;43GcRfs0~_F;bIi$wqL&}reM4SW05yjxFn*wu)c7l@Y3X#!ozkm|K+cV zZmvg0;8>5-xse_x53KeWX&3K-v56kFH|q&^oAefTa;_@exnEl-*X!iJ%9hkoMYp?} zD+}xR>I%mwdkED=^cQZOF+kXAXN`ZvHM76#KkSMgQ)&K*?zjp^p0yETdro*eRHuYq{%>r=JG!SFs19g9`0d64iRi!KgN~mf2|dbzau5sQchiAH{N?!ZyYZy)iX>{P zC>+XB+7+Veio%)tQL%JS^HDMK@uLR`s)LH5gNmAmAFWT?e#ItGQFTlGqT^b{?m1uQ z9o2Eg*74V6kzPd!ln((qK8Pe>DTlIgC?8c;oLYZ(U6gkwzQnh@+o$zY0_92IOZukg zMH2iKl|y-I<9DdLqUKdre*F$Pq%l$gKZ<~?`YEkPBuU}-=J2C*^Q*M`z4KMFyS9SM}|&(s!4WbmtS_#HJ(W2OWs0ZKp?1b&ryfW{$`$e|<$S!jgTK?zU-l)&#u zpyYT;ZIMIx1SkPY zfD-sI1ip2A|J}>~n5@xODFI4=5}*WrG=blJ{`*q=KRQ3OeM*25padv^ib>#0^9MaI zk|eI8a;TW9qFzw~lmI30?F1_7xJ&&KNs{}sbNF_J(i$iMN`Mk59|Ax7_)7g3NrdsE zIDAQ!U-Iiq<>`4!fD)htDk_05jc@e4NRm3r;nN%{s;;SDlmI0_34AL7I_`@k2!B-$ z->MHYstFOrCaa`-fdU%v;@ z2q^(d;8!I;$90h;wUk4}AYlS9Stq0~c4fD-sw3D9{(BuO3RKskto@V#{)5g+}a z1So-@lEC*KFKBxriB2eo--E+XIVe!4DFI4ALV%9bBFcd$Ky~nAbRZEQ{h$OWfuE89 z9XCaiyipF6gIEaPTL%*H(GN<168J3%eDCpwwkMM4jB@xrIQ*7_3JsbPpadiY=r}K; z9C!j$2W8WNL_G9^5}*Vqflm;is-A#6l>k1Bp232PHrWPy%HjP;xw>wn##e za`-hkl%Y$|B9s6nKnZ*m0Xk2JB%CORipk-tL`*ND1SkPY;0F+(^M*)5opLA}haVtW z+9V}F2~YwRf&iU2M3OvE4&}q4LWrC?K?zU-pCs_5aqp9bexrk5;zQ4iBx;}>%7?>m z)YWLHl)!f<@T14A?;fy>8~oAyX!{~bMt@fhGL|!~juM~*J|gg=$CZy<`6<8tXnwSP zkwiC?!>2j?l%0w?O$k&G0>AsX@})HV?tEzcB8fzPR}Nq5AM`vW@GBGeUHd(aTO^S+ z{~lLX4zuV^`ugeT=dIf#Yuv+6)1V){V|Py(MM@U!-7>bFRe zPs)LE5DTHKI*^Eneoz9xDS@)~8(K~zQ4HlkIf#YutLi`^a{56DPy(MM@T>NF8kb0t zZ_0sk5DTHKI*^Eneoz9Gz$XcmwO`S4A_-5*fpQQF;aAmxMC9~?5}*V=N#IxQ_cSh% zB;S++Q@%a_=ccl)${kt79_1LYtV0@VT4!Ox@vv9CXJ zqy0rB!B06*4q_ot9aI<{_(S8Ss+Gk_QINw*;=Mp{N$$g5W4fHcNT49l7Ss^v3j{4a zy*yib`hjPluTQJig1YhofewFR_u$CjfgzCs{!)QJReYdl?~pzNd-m-kP}SjWMM-7W z0(m((IX%gDfk3)v`D(@6-wg_8i|hEvPBZyn^G!*%#*6-GYp(jIZQk>rw!!0?fEB~}zZv4|eb?u*a zYV-$sAC3wH!yh}iirfERhDOQ>3}u=ji4^!2ql_W9BM9^hiR{+BhdcD`JD_{dKEaV80|g~J ztHR%vOZQ?^9DulK4OaYJL7>i8Ca%t_XGBEX;D|vXpA16W^`)Up_X0i`fgnhKv_^?O zFe9sjo8+YU0{TN4Pz_KGPz_KGPz_KGPz_KGPz_KGPz_KG{6-qch@Ij~pM~lB0IC71 z0jdG20jdG20jdG20jdG20jh!ju7L+=N1vyDqvvPZNB(;sq0A^VssXA2ssXA2ssXA2 zssXA2ssXA2ssXA2ssXA2ssXA2ssXA2ssXA2ssXA2ssXA2ssXA2ssXA2ssXA2ssXA2 zssXA2ssXA2ssXA2ssXA2ssXA2ssXBj3a0@<5?A4TqJN)3{~s4r15^W415^W415^W4 z15^W415^W415^W415^XQkp@0qrNk`b!tv&l=k7C8E zp77h>JKiH%uganOp#gz(FI^Yq@52J=x+qdxx-N>;madB;wWaH#NNwr5C{kOxE{fEa zu8ShIrR$!L_)>AEOVTe>ca)RwNgA+@FJqDbw(0`J`V zI3r8`PQ|*vSDW~LE7?drlCESUwWS%8E`K4-nADbLOlnIrCbgv*liJdZNo{Gyq_#9; zQd^oasV&Wz)RtyUYD+UFwWS%8+R}_kZE41&wlrf>TbePcowPMEwZgw~eX!9}ApZH$ zBdyer^6=&3x=Vil5pkD?4<+j?i(MWpO7o*EZIxD6S&x^+hfkIHT>GE8{^MgGmlYrX z*p}t@kK6n&-@n#}&(-<0b;#nn&-wAWb6?wb$vVEaoUE@a>BoPuW$jCO)%RawDtT>r z`6TOlOZr`!EpGR7=Va}dtm-SxXYuimW0F;S-?N_2#rE;J@99%{wDoZ;rF}1tcE5L> zrN#E8)QX~|L3k1`zt>Gz5M>K?R?Jn|8nlXw);J=|1WWr zy!Lzg`7Ldg^t&wfx3pj0Wy=yzX=TdW*Rs}Mn!n$2ysW;IXSr{Qr?j%=>2F!;F3nF_ z9WRSdto;QqpZryISg4m#+EN^RkYqWPRW2pRCH4 z^s785r^!S^4=b^~gG=Pu2G={>iHBr~H!jFM~vb+W$B|V>MD;oW#N~s>-b!pCENPkwI$D$Ec>(B ZCF7CWAIBj+_Obo5ewW53-B-Xk{tpQo)Yt$3 literal 0 HcmV?d00001 diff --git a/examples/wflow_piave_subbasin/wflow_sbm.toml b/examples/wflow_piave_subbasin/wflow_sbm.toml index 111ae13d..b7c302d0 100644 --- a/examples/wflow_piave_subbasin/wflow_sbm.toml +++ b/examples/wflow_piave_subbasin/wflow_sbm.toml @@ -24,7 +24,7 @@ gauges_grdc = "wflow_gauges_grdc" type = "sbm" masswasting = true snow = true -reinit = true +reinit = false reservoirs = true lakes = true glacier = true diff --git a/hydromt_wflow/wflow.py b/hydromt_wflow/wflow.py index 0bb53626..e51b7094 100644 --- a/hydromt_wflow/wflow.py +++ b/hydromt_wflow/wflow.py @@ -1579,6 +1579,148 @@ def setup_temp_pet_forcing( temp_out.attrs.update(opt_attr) self.set_forcing(temp_out.where(mask), name="temp") + def setup_cold_states( + self, + timestamp: str = None, + ) -> None: + """Setup cold states for Wflow. + To be run last as this requires some soil parameters or constant_pars to be computed already. + + To be run after setup_lakes, setup_reservoirs and setup_glaciers to also create + cold states for them if they are present in the basin. + + This function is mainly useful in case the wflow model is read into Delft-FEWS. + + Adds model layer: + + * **satwaterdepth**: saturated store [mm] + * **snow**: snow storage [mm] + * **tsoil**: top soil temperature [°C] + * **ustorelayerdepth**: amount of water in the unsaturated store, per layer [mm] + * **snowwater**: liquid water content in the snow pack [mm] + * **canopystorage**: canopy storage [mm] + * **q_river**: river discharge [m3/s] + * **h_river**: river water level [m] + * **h_av_river**: river average water level [m] + * **ssf**: subsurface flow [m3/d] + * **h_land**: land water level [m] + * **h_av_land**: land average water level[m] + * **q_land** or **qx_land**+**qy_land**: overland flow for kinwave [m3/s] or overland flow in x/y directions for local-inertial [m3/s] + + If lakes, also adds: + + * **waterlevel_lake**: lake water level [m] + + If reservoirs, also adds: + + * **volume_reservoir**: reservoir volume [m3] + + If glaciers, also adds: + + * **glacierstore**: water within the glacier [mm] + + Parameters + ---------- + timestamp : str, optional + Timestamp of the cold states. By default uses the (starttime - timestepsecs) from the config. + """ + dsin = self.staticmaps + timestepsecs = self.get_config("timestepsecs", fallback=86400) + dtype = "float32" + nodata = -999 + + ds_out = xr.Dataset(coords=dsin.raster.coords) + + def create_constant_map(dsin, value, nodata, dtype, maskname): + nodata = np.dtype(dtype).type(nodata) + da_param = xr.where(dsin[self._MAPS[maskname]], value, nodata).astype(dtype) + da_param.raster.set_nodata(nodata) + + return da_param + + # zeros (per layer for "ustorelayerdepth") + zeromap = ["tsoil", "snow", "snowwater", "canopystorage", "h_land", "h_av_land"] + olf = self.get_config("model.land_routing") + if olf == "local-inertial": + zeromap.extend(["qx_land", "qy_land"]) + else: + zeromap.extend(["q_land"]) + + for var in zeromap: + if var == "tsoil": + value = 10.0 + else: + value = 0.0 + da_param = create_constant_map( + dsin, value, nodata, dtype, maskname="basins" + ) + da_param = da_param.rename(var) + ds_out[var] = da_param + + # zeros for river + zeromap_riv = ["q_river", "h_river", "h_av_river"] + for var in zeromap_riv: + value = 0.0 + da_param = create_constant_map( + dsin, value, nodata, dtype, maskname="rivmsk" + ) + da_param = da_param.rename(var) + ds_out[var] = da_param + + # satwaterdepth + swd = 0.85 * dsin["SoilThickness"] * (dsin["thetaS"] - dsin["thetaR"]) + swd = create_constant_map(dsin, swd.values, nodata, dtype, maskname="basins") + ds_out["satwaterdepth"] = swd + + # ssf + zi = np.maximum( + 0.0, dsin["SoilThickness"] - swd / (dsin["thetaS"] - dsin["thetaR"]) + ) + kh0 = dsin["KsatHorFrac"] * dsin["KsatVer"] * 0.001 * (86400 / timestepsecs) + ssf = (kh0 * np.maximum(0.00001, dsin["Slope"]) / (dsin["f"] * 1000)) * ( + np.exp(-dsin["f"] * 1000 * zi * 0.001) + ) - ( + np.exp(-dsin["f"] * 1000 * dsin["SoilThickness"]) + * np.sqrt(dsin.raster.area_grid()) + ) + ssf = create_constant_map(dsin, ssf.values, nodata, dtype, maskname="basins") + ds_out["ssf"] = ssf + + # ustorelayerdepth (zero per layer) + usld = hydromt.raster.full_like(dsin["c"], nodata=nodata) + for sl in usld["layer"]: + usld.loc[dict(layer=sl)] = xr.where(dsin[self._MAPS["basins"]], 0.0, nodata) + ds_out["ustorelayerdepth"] = usld + + # reservoir + if "ResMaxVolume" in dsin: + resvol = dsin["ResTargetFullFrac"] * dsin["ResMaxVolume"] + resvol = xr.where(dsin[self._MAPS["reslocs"]] > 0, resvol, nodata) + resvol.raster.set_nodata(nodata) + ds_out["volume_reservoir"] = resvol + # lake + if "LakeAvgLevel" in dsin: + ds_out["waterlevel_lake"] = dsin["LakeAvgLevel"] + # glacier + if "G_SIfrac" in dsin: + glacstore = create_constant_map( + dsin, 5500.0, nodata, dtype, maskname="basins" + ) + ds_out["glacierstore"] = glacstore + + # Add time dimension + if timestamp is None: + starttime = pd.to_datetime(self.get_config("starttime")) + timestamp = starttime - pd.Timedelta(seconds=timestepsecs) + else: + timestamp = pd.to_datetime(timestamp) + ds_out = ds_out.expand_dims(dim=dict(time=[timestamp])) + + self.set_states(ds_out) + + # Update config to read the states + self.set_config("model.reinit", False) + # I/O def read(self): """Method to read the complete model schematization and configuration from file.""" @@ -1586,6 +1728,7 @@ def read(self): self.read_staticmaps() self.read_intbl() self.read_staticgeoms() + self.read_states() self.read_forcing() self.logger.info("Model read") @@ -1603,6 +1746,8 @@ def write(self): self.write_staticmaps() if self._staticgeoms: self.write_staticgeoms() + if self._states: + self.write_states() if self._forcing: self.write_forcing() @@ -1897,16 +2042,43 @@ def write_forcing( def read_states(self): """Read states at and parse to dict of xr.DataArray""" + fn_default = join(self.root, "instate", "instates.nc") + fn = self.get_config("state.path_input", abs_path=True, fallback=fn_default) if not self._write: # start fresh in read-only mode self._states = dict() - # raise NotImplementedError() + if fn is not None and isfile(fn): + self.logger.info(f"Read states from {fn}") + ds = xr.open_dataset(fn, mask_and_scale=False) + for v in ds.data_vars: + self.set_states(ds[v]) - def write_states(self): + def write_states(self, fn_out=None): """write states at in model ready format""" if not self._write: raise IOError("Model opened in read-only mode") - # raise NotImplementedError() + if self.states: + self.logger.info("Write states file") + + # get output filename + if fn_out is not None: + self.set_config("state.path_input", fn_out) + self.write_config() # re-write config + else: + fn_out = self.get_config("state.path_input", abs_path=True) + + # merge, process and write forcing + ds = xr.merge(self.states.values()) + + # make sure no _FillValue is written to the time dimension + ds["time"].attrs.pop("_FillValue", None) + + # Check if all sub-folders in fn_out exists and if not create them + if not isdir(dirname(fn_out)): + os.makedirs(dirname(fn_out)) + + # write states + ds.to_netcdf(fn_out, mode="w") def read_results(self): """Read results at and parse to dict of xr.DataArray/xr.Dataset""" @@ -2200,3 +2372,28 @@ def clip_forcing(self, crs=4326, **kwargs): self.staticmaps.raster.bounds ) self.set_forcing(ds_forcing) + + def clip_states(self, crs=4326, **kwargs): + """Return clippped states for subbasin. + + Returns + ------- + xarray.DataSet + Clipped states. + + """ + if len(self.states) > 0: + self.logger.info("Clipping NetCDF states..") + ds_states = xr.merge(self.states.values()).raster.clip_bbox( + self.staticmaps.raster.bounds + ) + # Check for reservoirs/lakes presence in the clipped model + remove_maps = [] + if self._MAPS["resareas"] not in self.staticmaps: + if "volume_reservoir" in ds_states: + remove_maps.extend(["volume_reservoir"]) + if self._MAPS["lakeareas"] not in self.staticmaps: + if "waterlevel_lake" in ds_states: + remove_maps.extend(["waterlevel_lake"]) + ds_states = ds_states.drop_vars(remove_maps) + self.set_states(ds_states) diff --git a/tests/data/wflow_piave_build_subbasin.ini b/tests/data/wflow_piave_build_subbasin.ini index 402a785f..0b4dbce6 100644 --- a/tests/data/wflow_piave_build_subbasin.ini +++ b/tests/data/wflow_piave_build_subbasin.ini @@ -78,3 +78,4 @@ dem_forcing_fn = era5_orography pet_method = debruin skip_pet = False +[setup_cold_states] \ No newline at end of file From a1990816bd743b2ab35b9ebaae0637993034f977 Mon Sep 17 00:00:00 2001 From: hboisgon Date: Mon, 11 Apr 2022 18:10:11 +0200 Subject: [PATCH 02/19] write_fews first version --- examples/wflow_piave_clip/wflow_sbm.toml | 6 +- examples/wflow_piave_subbasin/wflow_sbm.toml | 6 +- hydromt_wflow/data/wflow/wflow_sbm.toml | 6 +- hydromt_wflow/wflow.py | 119 ++++++++++++++++++- 4 files changed, 126 insertions(+), 11 deletions(-) diff --git a/examples/wflow_piave_clip/wflow_sbm.toml b/examples/wflow_piave_clip/wflow_sbm.toml index c4fd0d11..44c48577 100644 --- a/examples/wflow_piave_clip/wflow_sbm.toml +++ b/examples/wflow_piave_clip/wflow_sbm.toml @@ -74,7 +74,7 @@ f = "f" infiltcappath = "InfiltCapPath" infiltcapsoil = "InfiltCapSoil" kext = "Kext" -"kv₀" = "KsatVer" +kv_0 = "KsatVer" leaf_area_index = "LAI" m = "M_" maxleakage = "MaxLeakage" @@ -93,8 +93,8 @@ tti = "TTI" ttm = "TTM" water_holding_capacity = "WHC" waterfrac = "WaterFrac" -"θᵣ" = "thetaR" -"θₛ" = "thetaS" +theta_r = "thetaR" +theta_s = "thetaS" glacierstore = "wflow_glacierstore" glacierfrac = "wflow_glacierfrac" g_cfmax = "G_Cfmax" diff --git a/examples/wflow_piave_subbasin/wflow_sbm.toml b/examples/wflow_piave_subbasin/wflow_sbm.toml index b7c302d0..6e8e61f0 100644 --- a/examples/wflow_piave_subbasin/wflow_sbm.toml +++ b/examples/wflow_piave_subbasin/wflow_sbm.toml @@ -74,7 +74,7 @@ f = "f" infiltcappath = "InfiltCapPath" infiltcapsoil = "InfiltCapSoil" kext = "Kext" -"kv₀" = "KsatVer" +kv_0 = "KsatVer" leaf_area_index = "LAI" m = "M_" maxleakage = "MaxLeakage" @@ -93,8 +93,8 @@ tti = "TTI" ttm = "TTM" water_holding_capacity = "WHC" waterfrac = "WaterFrac" -"θᵣ" = "thetaR" -"θₛ" = "thetaS" +theta_r = "thetaR" +theta_s = "thetaS" glacierstore = "wflow_glacierstore" glacierfrac = "wflow_glacierfrac" g_cfmax = "G_Cfmax" diff --git a/hydromt_wflow/data/wflow/wflow_sbm.toml b/hydromt_wflow/data/wflow/wflow_sbm.toml index 65afc804..e1aa3597 100644 --- a/hydromt_wflow/data/wflow/wflow_sbm.toml +++ b/hydromt_wflow/data/wflow/wflow_sbm.toml @@ -70,7 +70,7 @@ f = "f" infiltcappath = "InfiltCapPath" infiltcapsoil = "InfiltCapSoil" kext = "Kext" -"kv₀" = "KsatVer" +kv_0 = "KsatVer" leaf_area_index = "LAI" # TODO support cyclic m = "M_" maxleakage = "MaxLeakage" @@ -89,8 +89,8 @@ tti = "TTI" ttm = "TTM" water_holding_capacity = "WHC" waterfrac = "WaterFrac" -"θᵣ" = "thetaR" -"θₛ" = "thetaS" +theta_r = "thetaR" +theta_s = "thetaS" [input.lateral.river] length = "wflow_riverlength" diff --git a/hydromt_wflow/wflow.py b/hydromt_wflow/wflow.py index e51b7094..2938d3af 100644 --- a/hydromt_wflow/wflow.py +++ b/hydromt_wflow/wflow.py @@ -5,6 +5,7 @@ from os.path import join, dirname, basename, isfile, isdir from typing import Optional import glob +import shutil import numpy as np import pandas as pd import geopandas as gpd @@ -1751,6 +1752,117 @@ def write(self): if self._forcing: self.write_forcing() + def write_fews( + self, + fews_root: str, + scheme_version: int, + region_name: str, + model_version: int, + ) -> None: + """ + Method to write and export the complete model schematization and configuration to a Delft-FEWS configuration. + + Writes: + + * zipped wflow model in ModuleDataSetFiles + * staticgeoms in MapLayerFiles + * states in ColdStateFiles + * forcing in Import + + Parameters + ---------- + fews_root: str + Path to the FEWS configuration. + scheme_version: int + Version number of the modelling scheme (coupled model suite). + region_name: str + Name of the model region. + model_version: int + Version of the current wflow model version for region_name. + + """ + self.logger.info(f"Write model data to {fews_root}") + # Location of wflow model + wflow_root = os.path.join( + fews_root, + "Config", + "ModuleDataSetFiles", + f"scheme.{scheme_version}", + f"{region_name}.wflow.{model_version}", + ) + self.set_root(wflow_root) + + # Use standard ouput filenames + toml_opt = { + "state.path_input": "instate/instates.nc", + "state.path_output": "run_default/outstate/outstates.nc", + "input.path_static": "staticmaps.nc", + "output.path": "run_default/output.nc", + "netcdf.path": "run_default/output_scalar.nc", + "csv": "run_default/output.csv", + } + for option in toml_opt: + self.set_config(option, toml_opt[option]) + + self.write_data_catalog() + if self._staticmaps: + self.write_staticmaps() + + # Write staticgeoms in MapLayerFiles folder + if self._staticgeoms: + geoms_root = os.path.join( + fews_root, + "Config", + "MapLayerFiles", + f"scheme.{scheme_version}", + f"{region_name}.wflow.{model_version}", + ) + self.write_staticgeoms(geoms_root=geoms_root) + + # Write states in ColdStateFiles folder + if not self._states: + self.setup_cold_states() + states_fn = os.path.join( + fews_root, + "Config", + "ColdStateFiles", + f"scheme.{scheme_version}", + f"{region_name}.wflow.{model_version}", + "instates.nc", + ) + self.write_states(fn_out=states_fn) + + # Write forcing in another folder + if self._forcing: + forcing_name = os.path.basename(self.get_config("input.path_forcing")) + forcing_fn = os.path.join( + fews_root, + "Import", + f"scheme.{scheme_version}", + f"{region_name}.wflow.{model_version}", + forcing_name, + ) + self.write_forcing(fn_out=forcing_fn) + + # Update template config and write in toml_template folder + toml_opt = { + "starttime": "%START_DATE_TIME%", + "endtime": "%END_DATE_TIME%", + "input.path_forcing": "inmaps.nc", + } + for option in toml_opt: + self.set_config(option, toml_opt[option]) + + self.write_config( + config_root=os.path.join(wflow_root, "toml_template"), + config_name="wflow_sbm.toml", + ) + + # Zip the model and erase the unzipped copy + wflow_root_zip = wflow_root + ".zip" + shutil.make_archive(wflow_root_zip, "zip", wflow_root) + shutil.rmtree(wflow_root) + def read_staticmaps(self, **kwargs): """Read staticmaps""" fn_default = join(self.root, "staticmaps.nc") @@ -1868,7 +1980,7 @@ def read_staticgeoms(self): if name != "region": self.set_staticgeoms(gpd.read_file(fn), name=name) - def write_staticgeoms(self): + def write_staticgeoms(self, geoms_root=None): """Write staticmaps at in model ready format""" # to write use self.staticgeoms[var].to_file() if not self._write: @@ -1876,7 +1988,10 @@ def write_staticgeoms(self): if self.staticgeoms: self.logger.info("Writing model staticgeom to file.") for name, gdf in self.staticgeoms.items(): - fn_out = join(self.root, "staticgeoms", f"{name}.geojson") + if geoms_root: + fn_out = join(geoms_root, f"{name}.geojson") + else: + fn_out = join(self.root, "staticgeoms", f"{name}.geojson") gdf.to_file(fn_out, driver="GeoJSON") def read_forcing(self): From 7930df2e9c08d13d28f3f44436c82e4011fd9a81 Mon Sep 17 00:00:00 2001 From: hboisgon Date: Fri, 29 Apr 2022 18:08:30 +0200 Subject: [PATCH 03/19] Update write_fews function --- hydromt_wflow/wflow.py | 102 ++++++++++++++++++++++++++++------------- 1 file changed, 71 insertions(+), 31 deletions(-) diff --git a/hydromt_wflow/wflow.py b/hydromt_wflow/wflow.py index 2938d3af..e70b5787 100644 --- a/hydromt_wflow/wflow.py +++ b/hydromt_wflow/wflow.py @@ -23,6 +23,7 @@ from hydromt.models.model_api import Model from hydromt import flw from hydromt.io import open_mfraster +from hydromt.cf_utils import FewsUtils from . import utils, workflows, DATADIR @@ -1758,6 +1759,8 @@ def write_fews( scheme_version: int, region_name: str, model_version: int, + fews_template: str = None, + wflow_template: str = None, ) -> None: """ Method to write and export the complete model schematization and configuration to a Delft-FEWS configuration. @@ -1779,19 +1782,34 @@ def write_fews( Name of the model region. model_version: int Version of the current wflow model version for region_name. - + fews_template: str, Path, optional + Path to a FEWS config template for initialisation. If None, download from url. + wflow_template: str, Path, optional + Path to a folder containing all wflow template files. If None download from url. """ + if self._read: + self.read() + self.logger.info(f"Setting FEWS config at {fews_root}") + fews = FewsUtils(fews_root, template_path=fews_template) + # Instantiate the wflow model in fews object + model_name = f"wflow.{region_name}.{model_version}" + fews.add_modeldata( + name=model_name, + scheme_version=scheme_version, + crs=self.crs, + shape=self.staticmaps.raster.shape, + bounds=self.staticmaps.raster.bounds, + ) + + # Update and write wflow model components in specific FEWS folders and format self.logger.info(f"Write model data to {fews_root}") # Location of wflow model wflow_root = os.path.join( - fews_root, - "Config", - "ModuleDataSetFiles", + fews.module_path, f"scheme.{scheme_version}", - f"{region_name}.wflow.{model_version}", + f"wflow.{region_name}.{model_version}", ) - self.set_root(wflow_root) - + self.set_root(wflow_root, mode="w") # Use standard ouput filenames toml_opt = { "state.path_input": "instate/instates.nc", @@ -1803,64 +1821,86 @@ def write_fews( } for option in toml_opt: self.set_config(option, toml_opt[option]) - self.write_data_catalog() if self._staticmaps: self.write_staticmaps() # Write staticgeoms in MapLayerFiles folder if self._staticgeoms: + # Write first in zip folder + self.write_staticgeoms() + # Write a copy in MapLayer with different name geoms_root = os.path.join( - fews_root, - "Config", - "MapLayerFiles", + fews.map_path, f"scheme.{scheme_version}", - f"{region_name}.wflow.{model_version}", + f"wflow.{region_name}.{model_version}", ) + if not isdir(geoms_root): + os.makedirs(geoms_root) + names = list(self.staticgeoms.keys()) + for name in names: + new_name = f"wflow.{region_name}.{model_version}_{name}" + self._staticgeoms[new_name] = self._staticgeoms.pop(name) self.write_staticgeoms(geoms_root=geoms_root) # Write states in ColdStateFiles folder if not self._states: self.setup_cold_states() states_fn = os.path.join( - fews_root, - "Config", - "ColdStateFiles", + fews.state_path, f"scheme.{scheme_version}", - f"{region_name}.wflow.{model_version}", + f"wflow.{region_name}.{model_version}", "instates.nc", ) self.write_states(fn_out=states_fn) - # Write forcing in another folder + # Write forcing and wflow_dem in another folder + import_path = os.path.join( + fews.import_path, + f"scheme.{scheme_version}", + f"wflow.{region_name}.{model_version}", + ) + if not isdir(import_path): + os.makedirs(import_path) if self._forcing: forcing_name = os.path.basename(self.get_config("input.path_forcing")) - forcing_fn = os.path.join( - fews_root, - "Import", - f"scheme.{scheme_version}", - f"{region_name}.wflow.{model_version}", - forcing_name, - ) - self.write_forcing(fn_out=forcing_fn) + self.write_forcing(fn_out=os.path.join(import_path, forcing_name)) + if "wflow_dem" in self.staticmaps: + da = self.staticmaps["wflow_dem"] + da.to_netcdf(os.path.join(import_path, "wflow_dem.nc")) - # Update template config and write in toml_template folder + # Update fews times and write in toml_template folder toml_opt = { "starttime": "%START_DATE_TIME%", "endtime": "%END_DATE_TIME%", "input.path_forcing": "inmaps.nc", + "state.path_input": "instate/instates.nc", } for option in toml_opt: self.set_config(option, toml_opt[option]) - + config_root = os.path.join(wflow_root) # , "toml_template") + if not isdir(config_root): + os.makedirs(config_root) self.write_config( - config_root=os.path.join(wflow_root, "toml_template"), - config_name="wflow_sbm.toml", + config_root=config_root, + config_name="wflow_sbm_template.toml", + ) + + # Add FEWS config file for the model + self.logger.info("Adding FEWS template files for Wflow") + fews.add_template_configfiles( + model_source=model_name, model_templates=wflow_template ) + # Updating csv locs files + fews.add_locationsfiles(model_source=model_name) - # Zip the model and erase the unzipped copy - wflow_root_zip = wflow_root + ".zip" + # Close logger, Zip the model and erase the unzipped copy + self.logger.info("Zipping wflow model") + wflow_root_zip = wflow_root shutil.make_archive(wflow_root_zip, "zip", wflow_root) + for handler in self.logger.handlers[:]: + handler.close() + logger.removeHandler(handler) shutil.rmtree(wflow_root) def read_staticmaps(self, **kwargs): From 3fdbbdd3490f6134658957ff029a773d3bd41c00 Mon Sep 17 00:00:00 2001 From: "xiaohan.li-deltares" Date: Thu, 16 Jun 2022 15:06:27 +0200 Subject: [PATCH 04/19] add copy location csv to fews --- hydromt_wflow/wflow.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hydromt_wflow/wflow.py b/hydromt_wflow/wflow.py index e70b5787..96952018 100644 --- a/hydromt_wflow/wflow.py +++ b/hydromt_wflow/wflow.py @@ -1785,7 +1785,7 @@ def write_fews( fews_template: str, Path, optional Path to a FEWS config template for initialisation. If None, download from url. wflow_template: str, Path, optional - Path to a folder containing all wflow template files. If None download from url. + Path to a folder containing all wflow template files (xml). If None download from url. """ if self._read: self.read() @@ -1892,7 +1892,7 @@ def write_fews( model_source=model_name, model_templates=wflow_template ) # Updating csv locs files - fews.add_locationsfiles(model_source=model_name) + fews.add_locationsfiles(model_source=model_name, model_templates=wflow_template) # FIXME this does not write correctly # Close logger, Zip the model and erase the unzipped copy self.logger.info("Zipping wflow model") From 7a52e86579caf5e8ec696e367ada2a29910a77be Mon Sep 17 00:00:00 2001 From: "xiaohan.li-deltares" Date: Mon, 20 Jun 2022 18:25:51 +0200 Subject: [PATCH 05/19] remove csv (bug) and scalar output (not used); update write coldstate --- hydromt_wflow/wflow.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/hydromt_wflow/wflow.py b/hydromt_wflow/wflow.py index 96952018..d260c590 100644 --- a/hydromt_wflow/wflow.py +++ b/hydromt_wflow/wflow.py @@ -1816,8 +1816,8 @@ def write_fews( "state.path_output": "run_default/outstate/outstates.nc", "input.path_static": "staticmaps.nc", "output.path": "run_default/output.nc", - "netcdf.path": "run_default/output_scalar.nc", - "csv": "run_default/output.csv", + # "netcdf.path": "run_default/output_scalar.nc", # do not need scalar data yet + # "csv": "run_default/output.csv", # do not need scalar data yet --> gives an error in model } for option in toml_opt: self.set_config(option, toml_opt[option]) @@ -1846,13 +1846,14 @@ def write_fews( # Write states in ColdStateFiles folder if not self._states: self.setup_cold_states() - states_fn = os.path.join( + states_root = states_fn = os.path.join( fews.state_path, - f"scheme.{scheme_version}", - f"wflow.{region_name}.{model_version}", + f"run_wflow.{region_name}.{model_version} Default") + states_fn = os.path.join( + states_root, "instates.nc", ) - self.write_states(fn_out=states_fn) + self.write_states(fn_out=states_fn) # Write forcing and wflow_dem in another folder import_path = os.path.join( @@ -1871,8 +1872,8 @@ def write_fews( # Update fews times and write in toml_template folder toml_opt = { - "starttime": "%START_DATE_TIME%", - "endtime": "%END_DATE_TIME%", + "starttime": "%START_DATE_TIME(date)%T%START_DATE_TIME(time)%", + "endtime": "%END_DATE_TIME(date)%T%END_DATE_TIME(time)%", "input.path_forcing": "inmaps.nc", "state.path_input": "instate/instates.nc", } @@ -1894,14 +1895,17 @@ def write_fews( # Updating csv locs files fews.add_locationsfiles(model_source=model_name, model_templates=wflow_template) # FIXME this does not write correctly - # Close logger, Zip the model and erase the unzipped copy + # Close logger, Zip the model and state, and erase the unzipped copy self.logger.info("Zipping wflow model") wflow_root_zip = wflow_root shutil.make_archive(wflow_root_zip, "zip", wflow_root) + states_root_zip = states_root + shutil.make_archive(states_root_zip, "zip", states_root) for handler in self.logger.handlers[:]: handler.close() logger.removeHandler(handler) shutil.rmtree(wflow_root) + shutil.rmtree(states_root) def read_staticmaps(self, **kwargs): """Read staticmaps""" From 7cc23d858b0d5414d665c7ff8b14274521991bbc Mon Sep 17 00:00:00 2001 From: "xiaohan.li-deltares" Date: Thu, 23 Jun 2022 10:20:48 +0200 Subject: [PATCH 06/19] example with write_fews function --- examples/wflow_build.ini | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/examples/wflow_build.ini b/examples/wflow_build.ini index 4fa6af75..3026172e 100644 --- a/examples/wflow_build.ini +++ b/examples/wflow_build.ini @@ -82,4 +82,12 @@ TTM = 0 WHC = 0.1 G_Cfmax = 5.3 G_SIfrac = 0.002 -G_TT = 1.3 \ No newline at end of file +G_TT = 1.3 + +[write_fews] +fews_root = d:\Projects\HYDROMT-CF\test +scheme_version = 1 +region_name = piave +model_version = 1 +fews_template = d:\Projects\HYDROMT-CF\initialstart\ +wflow_template = d:\Projects\HYDROMT-CF\templates\wflow\ From 3bddd0a306ce3a26ba945e085326b2a57842a102 Mon Sep 17 00:00:00 2001 From: "xiaohan.li-deltares" Date: Fri, 8 Jul 2022 16:46:26 +0200 Subject: [PATCH 07/19] add spatial display functions; rename cold state --- hydromt_wflow/wflow.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/hydromt_wflow/wflow.py b/hydromt_wflow/wflow.py index d260c590..c8f37d79 100644 --- a/hydromt_wflow/wflow.py +++ b/hydromt_wflow/wflow.py @@ -1848,7 +1848,7 @@ def write_fews( self.setup_cold_states() states_root = states_fn = os.path.join( fews.state_path, - f"run_wflow.{region_name}.{model_version} Default") + f"run_update_wflow.{region_name}.{model_version} Default") states_fn = os.path.join( states_root, "instates.nc", @@ -1893,7 +1893,13 @@ def write_fews( model_source=model_name, model_templates=wflow_template ) # Updating csv locs files - fews.add_locationsfiles(model_source=model_name, model_templates=wflow_template) # FIXME this does not write correctly + fews.add_locationsfiles(model_source=model_name, model_templates=wflow_template) + + # updating SpatialDisplay.xml + fews.add_spatialplots(model_source=model_name) + + # updating Topology.xml + # fews.add_topologygroup(model_source=model_name) # Close logger, Zip the model and state, and erase the unzipped copy self.logger.info("Zipping wflow model") From a861c7ee89f27da0c0837573b9f3a4b247339c62 Mon Sep 17 00:00:00 2001 From: "xiaohan.li-deltares" Date: Thu, 14 Jul 2022 14:54:23 +0200 Subject: [PATCH 08/19] write_fews: add topology groups --- hydromt_wflow/wflow.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/hydromt_wflow/wflow.py b/hydromt_wflow/wflow.py index c8f37d79..f612fdf3 100644 --- a/hydromt_wflow/wflow.py +++ b/hydromt_wflow/wflow.py @@ -1887,11 +1887,15 @@ def write_fews( config_name="wflow_sbm_template.toml", ) - # Add FEWS config file for the model + # Add FEWS config files for the model self.logger.info("Adding FEWS template files for Wflow") fews.add_template_configfiles( model_source=model_name, model_templates=wflow_template ) + + # update FEWS config files for the model + self.logger.info("Updating FEWS config files for Wflow") + # Updating csv locs files fews.add_locationsfiles(model_source=model_name, model_templates=wflow_template) @@ -1899,7 +1903,7 @@ def write_fews( fews.add_spatialplots(model_source=model_name) # updating Topology.xml - # fews.add_topologygroup(model_source=model_name) + fews.add_topologygroups(model_source=model_name) # Close logger, Zip the model and state, and erase the unzipped copy self.logger.info("Zipping wflow model") @@ -1913,6 +1917,8 @@ def write_fews( shutil.rmtree(wflow_root) shutil.rmtree(states_root) + + def read_staticmaps(self, **kwargs): """Read staticmaps""" fn_default = join(self.root, "staticmaps.nc") From 8eea6cb55afe056affc99894b63851968055f7ec Mon Sep 17 00:00:00 2001 From: hboisgon Date: Mon, 25 Jul 2022 11:59:33 +0200 Subject: [PATCH 09/19] Small changes --- hydromt_wflow/wflow.py | 59 +++++++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 21 deletions(-) diff --git a/hydromt_wflow/wflow.py b/hydromt_wflow/wflow.py index f612fdf3..bac6a46a 100644 --- a/hydromt_wflow/wflow.py +++ b/hydromt_wflow/wflow.py @@ -17,6 +17,7 @@ from pyflwdir import core_d8, core_ldd, core_conversion from dask.diagnostics import ProgressBar import logging +from typing import List # from dask.distributed import LocalCluster, Client, performance_report import hydromt @@ -676,17 +677,18 @@ def setup_laimaps(self, lai_fn="model_lai"): def setup_gauges( self, - gauges_fn="grdc", - source_gdf=None, - index_col=None, - snap_to_river=True, + gauges_fn: str = "grdc", + source_gdf: gpd.GeoDataFrame = None, + index_col: str = None, + snap_to_river: bool = True, mask=None, - derive_subcatch=False, - derive_outlet=True, - basename=None, - update_toml=True, - gauge_toml_header=None, - gauge_toml_param=None, + derive_subcatch: bool = False, + derive_outlet: bool = True, + basename: str = None, + update_toml: bool = True, + gauge_toml_type: str = "csv", + gauge_toml_header: List = None, + gauge_toml_param: List = None, **kwargs, ): """This components sets the default gauge map based on basin outlets and additional @@ -728,7 +730,9 @@ def setup_gauges( basename : str, optional Map name in staticmaps (wflow_gauges_basename), if None use the gauges_fn basename. update_toml : boolean, optional - Update [outputcsv] section of wflow toml file. + Update [csv.column] or [netcdf.variable] section of wflow toml file, based on update_toml_type. + gauge_toml_type : str, optional + Specify in which toml section to save the gauge ouputs. Either "csv" (default) or "netcdf". gauge_toml_header : list, optional Save specific model parameters in csv section. This option defines the header of the csv file./ By default saves Q (for lateral.river.q_av) and P (for vertical.precipitation). @@ -851,16 +855,31 @@ def setup_gauges( if update_toml: self.set_config(f"input.gauges_{basename}", f"{mapname}") - if self.get_config("csv") is not None: + if self.get_config("csv") is not None and gauge_toml_type == "csv": for o in range(len(gauge_toml_param)): gauge_toml_dict = { "header": gauge_toml_header[o], "map": f"gauges_{basename}", "parameter": gauge_toml_param[o], } - # If the gauge outcsv column already exists skip writting twice + # If the gauge csv column already exists skip writting twice if gauge_toml_dict not in self.config["csv"]["column"]: self.config["csv"]["column"].append(gauge_toml_dict) + elif ( + self.get_config("netcdf") is not None + and gauge_toml_type == "netcdf" + ): + for o in range(len(gauge_toml_param)): + gauge_toml_dict = { + "name": gauge_toml_header[o], + "map": f"gauges_{basename}", + "parameter": gauge_toml_param[o], + } + # If the gauge netdcf variable already exists skip writting twice + if gauge_toml_dict not in self.config["netdcf"]["variable"]: + self.config["netcdf"]["variable"].append( + gauge_toml_dict + ) self.logger.info(f"Gauges map from {basename} added.") # add subcatch @@ -1816,8 +1835,8 @@ def write_fews( "state.path_output": "run_default/outstate/outstates.nc", "input.path_static": "staticmaps.nc", "output.path": "run_default/output.nc", - # "netcdf.path": "run_default/output_scalar.nc", # do not need scalar data yet - # "csv": "run_default/output.csv", # do not need scalar data yet --> gives an error in model + "netcdf.path": "run_default/output_scalar.nc", + "csv.path": "run_default/output.csv", } for option in toml_opt: self.set_config(option, toml_opt[option]) @@ -1846,14 +1865,14 @@ def write_fews( # Write states in ColdStateFiles folder if not self._states: self.setup_cold_states() - states_root = states_fn = os.path.join( - fews.state_path, - f"run_update_wflow.{region_name}.{model_version} Default") + states_root = os.path.join( + fews.state_path, f"run_update_wflow.{region_name}.{model_version} Default" + ) states_fn = os.path.join( states_root, "instates.nc", ) - self.write_states(fn_out=states_fn) + self.write_states(fn_out=states_fn) # Write forcing and wflow_dem in another folder import_path = os.path.join( @@ -1917,8 +1936,6 @@ def write_fews( shutil.rmtree(wflow_root) shutil.rmtree(states_root) - - def read_staticmaps(self, **kwargs): """Read staticmaps""" fn_default = join(self.root, "staticmaps.nc") From 5b2263ad5ef519837f0e93d909c034c90a31ee51 Mon Sep 17 00:00:00 2001 From: hboisgon Date: Mon, 25 Jul 2022 13:26:08 +0200 Subject: [PATCH 10/19] Fix empty root folder if write_fews only in build mode --- hydromt_wflow/wflow.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/hydromt_wflow/wflow.py b/hydromt_wflow/wflow.py index bac6a46a..40dcf10e 100644 --- a/hydromt_wflow/wflow.py +++ b/hydromt_wflow/wflow.py @@ -1778,8 +1778,8 @@ def write_fews( scheme_version: int, region_name: str, model_version: int, - fews_template: str = None, - wflow_template: str = None, + fews_template: Optional[str] = None, + wflow_template: Optional[str] = None, ) -> None: """ Method to write and export the complete model schematization and configuration to a Delft-FEWS configuration. @@ -1806,8 +1806,8 @@ def write_fews( wflow_template: str, Path, optional Path to a folder containing all wflow template files (xml). If None download from url. """ - if self._read: - self.read() + # if self._read: # normally done by model_api in update mode + # self.read() self.logger.info(f"Setting FEWS config at {fews_root}") fews = FewsUtils(fews_root, template_path=fews_template) # Instantiate the wflow model in fews object @@ -1819,9 +1819,9 @@ def write_fews( shape=self.staticmaps.raster.shape, bounds=self.staticmaps.raster.bounds, ) - # Update and write wflow model components in specific FEWS folders and format self.logger.info(f"Write model data to {fews_root}") + old_root = self.root # Location of wflow model wflow_root = os.path.join( fews.module_path, @@ -1829,6 +1829,14 @@ def write_fews( f"wflow.{region_name}.{model_version}", ) self.set_root(wflow_root, mode="w") + # In build mode, check if empty root folder (no staticmaps) was created and if yes remove + if not isfile(join(old_root, "staticmaps.nc")): + self.logger.info( + "Wflow model only saved directly in FEWS. To get a copy in {old_root}, use the 'write' function before 'write_fews'." + ) + # Delete empty folder at self.root + shutil.rmtree(old_root) + # Use standard ouput filenames toml_opt = { "state.path_input": "instate/instates.nc", From 974324d5f3e0383d04b850dc5f59436a256368ba Mon Sep 17 00:00:00 2001 From: "xiaohan.li-deltares" Date: Mon, 25 Jul 2022 13:30:15 +0200 Subject: [PATCH 11/19] update explorer with extra extent --- hydromt_wflow/wflow.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hydromt_wflow/wflow.py b/hydromt_wflow/wflow.py index 40dcf10e..b28ff088 100644 --- a/hydromt_wflow/wflow.py +++ b/hydromt_wflow/wflow.py @@ -1932,6 +1932,8 @@ def write_fews( # updating Topology.xml fews.add_topologygroups(model_source=model_name) + # updating Explorer.xml + fews.update_explorer(model_source=model_name) # Close logger, Zip the model and state, and erase the unzipped copy self.logger.info("Zipping wflow model") wflow_root_zip = wflow_root From 91461a5d27c7a8d34aeb97d86cd1b2321d6bb926 Mon Sep 17 00:00:00 2001 From: hboisgon Date: Mon, 25 Jul 2022 16:28:58 +0200 Subject: [PATCH 12/19] Select output variables for spatial display --- hydromt_wflow/wflow.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/hydromt_wflow/wflow.py b/hydromt_wflow/wflow.py index b28ff088..2871fc50 100644 --- a/hydromt_wflow/wflow.py +++ b/hydromt_wflow/wflow.py @@ -1914,10 +1914,29 @@ def write_fews( config_name="wflow_sbm_template.toml", ) + # Get list of output variables from toml + outputvars = [] + if self.get_config("output.vertical"): + outputvars.extend([i for i in self.get_config("output.vertical")]) + if self.get_config("output.lateral.river"): + outputvars.extend( + [f"river.{i}" for i in self.get_config("output.lateral.river")] + ) + if self.get_config("output.lateral.land"): + outputvars.extend( + [f"land.{i}" for i in self.get_config("output.lateral.land")] + ) + if self.get_config("output.lateral.subsurface"): + outputvars.extend( + [f"land.{i}" for i in self.get_config("output.lateral.subsurface")] + ) + # Add FEWS config files for the model self.logger.info("Adding FEWS template files for Wflow") fews.add_template_configfiles( - model_source=model_name, model_templates=wflow_template + model_source=model_name, + model_templates=wflow_template, + variables=outputvars, ) # update FEWS config files for the model From 4b9fa2818eed47d276650cfa1ae0c02070f5a3e4 Mon Sep 17 00:00:00 2001 From: Indra Marth Date: Mon, 25 Jul 2022 17:02:28 +0200 Subject: [PATCH 13/19] Create FEWS.lnk in fews root --- hydromt_wflow/wflow.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hydromt_wflow/wflow.py b/hydromt_wflow/wflow.py index 2871fc50..ba4a04d8 100644 --- a/hydromt_wflow/wflow.py +++ b/hydromt_wflow/wflow.py @@ -1780,6 +1780,7 @@ def write_fews( model_version: int, fews_template: Optional[str] = None, wflow_template: Optional[str] = None, + fews_binaries: Optional[str] = None, ) -> None: """ Method to write and export the complete model schematization and configuration to a Delft-FEWS configuration. @@ -1805,6 +1806,8 @@ def write_fews( Path to a FEWS config template for initialisation. If None, download from url. wflow_template: str, Path, optional Path to a folder containing all wflow template files (xml). If None download from url. + fews_binaries: str, Path, optional + Path to a folder containing the FEWS binaries. Id None, assume FEWS bin folder in fews root. """ # if self._read: # normally done by model_api in update mode # self.read() @@ -1965,6 +1968,9 @@ def write_fews( shutil.rmtree(wflow_root) shutil.rmtree(states_root) + # create shortcut + fews.create_cf_link(fews_binaries=fews_binaries) + def read_staticmaps(self, **kwargs): """Read staticmaps""" fn_default = join(self.root, "staticmaps.nc") From 15a4482f6b81f1353ae6a754b2df5d42e90818f2 Mon Sep 17 00:00:00 2001 From: "xiaohan.li-deltares" Date: Mon, 25 Jul 2022 17:26:55 +0200 Subject: [PATCH 14/19] update global properties --- hydromt_wflow/wflow.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hydromt_wflow/wflow.py b/hydromt_wflow/wflow.py index ba4a04d8..ef9cc6e8 100644 --- a/hydromt_wflow/wflow.py +++ b/hydromt_wflow/wflow.py @@ -1821,6 +1821,7 @@ def write_fews( crs=self.crs, shape=self.staticmaps.raster.shape, bounds=self.staticmaps.raster.bounds, + T0=self.get_config("starttime"), ) # Update and write wflow model components in specific FEWS folders and format self.logger.info(f"Write model data to {fews_root}") @@ -1956,6 +1957,10 @@ def write_fews( # updating Explorer.xml fews.update_explorer(model_source=model_name) + # update global.properties + fews.update_globalproperties( + model_source=model_name, model_templates=wflow_template + ) # Close logger, Zip the model and state, and erase the unzipped copy self.logger.info("Zipping wflow model") wflow_root_zip = wflow_root From cce814611a169b55fa426668091d46e7c6da1f84 Mon Sep 17 00:00:00 2001 From: "xiaohan.li-deltares" Date: Mon, 25 Jul 2022 18:02:37 +0200 Subject: [PATCH 15/19] force read forcing during write fews --- hydromt_wflow/wflow.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hydromt_wflow/wflow.py b/hydromt_wflow/wflow.py index ef9cc6e8..8b04332a 100644 --- a/hydromt_wflow/wflow.py +++ b/hydromt_wflow/wflow.py @@ -1809,8 +1809,8 @@ def write_fews( fews_binaries: str, Path, optional Path to a folder containing the FEWS binaries. Id None, assume FEWS bin folder in fews root. """ - # if self._read: # normally done by model_api in update mode - # self.read() + if self._read: # force the function to read forcing + self.read_forcing() self.logger.info(f"Setting FEWS config at {fews_root}") fews = FewsUtils(fews_root, template_path=fews_template) # Instantiate the wflow model in fews object From 08331d6a047e50521fcb043cc13541c693931d29 Mon Sep 17 00:00:00 2001 From: hboisgon Date: Fri, 14 Apr 2023 17:37:20 +0800 Subject: [PATCH 16/19] get var names from toml in setup_cold_states --- hydromt_wflow/wflow.py | 53 ++++++++++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 17 deletions(-) diff --git a/hydromt_wflow/wflow.py b/hydromt_wflow/wflow.py index 8b04332a..3f204ba3 100644 --- a/hydromt_wflow/wflow.py +++ b/hydromt_wflow/wflow.py @@ -1688,46 +1688,65 @@ def create_constant_map(dsin, value, nodata, dtype, maskname): da_param = da_param.rename(var) ds_out[var] = da_param + # get soil variable names from config + st_vn = self.get_config("input.vertical.soilthickness", "SoilThickness") + ts_vn = self.get_config("input.vertical.theta_s", "thetaS") + tr_vn = self.get_config("input.vertical.theta_r", "thetaR") + ksh_vn = self.get_config("input.lateral.subsurface.ksathorfrac", "KsatHorFrac") + ksv_vn = self.get_config("input.vertical.kv_0", "KsatVer") + f_vn = self.get_config("input.vertical.f", "f") + c_vn = self.get_config("input.vertical.c", "c") + sl_vn = self.get_config("input.lateral.land.slope", "Slope") + # satwaterdepth - swd = 0.85 * dsin["SoilThickness"] * (dsin["thetaS"] - dsin["thetaR"]) + swd = 0.85 * dsin[st_vn] * (dsin[ts_vn] - dsin[tr_vn]) swd = create_constant_map(dsin, swd.values, nodata, dtype, maskname="basins") ds_out["satwaterdepth"] = swd # ssf zi = np.maximum( - 0.0, dsin["SoilThickness"] - swd / (dsin["thetaS"] - dsin["thetaR"]) + 0.0, dsin[st_vn] - swd / (dsin[ts_vn] - dsin[tr_vn]) ) - kh0 = dsin["KsatHorFrac"] * dsin["KsatVer"] * 0.001 * (86400 / timestepsecs) - ssf = (kh0 * np.maximum(0.00001, dsin["Slope"]) / (dsin["f"] * 1000)) * ( - np.exp(-dsin["f"] * 1000 * zi * 0.001) + kh0 = dsin[ksh_vn] * dsin[ksv_vn] * 0.001 * (86400 / timestepsecs) + ssf = (kh0 * np.maximum(0.00001, dsin[sl_vn]) / (dsin[f_vn] * 1000)) * ( + np.exp(-dsin[f_vn] * 1000 * zi * 0.001) ) - ( - np.exp(-dsin["f"] * 1000 * dsin["SoilThickness"]) + np.exp(-dsin[f_vn] * 1000 * dsin[st_vn]) * np.sqrt(dsin.raster.area_grid()) ) ssf = create_constant_map(dsin, ssf.values, nodata, dtype, maskname="basins") ds_out["ssf"] = ssf # ustorelayerdepth (zero per layer) - usld = hydromt.raster.full_like(dsin["c"], nodata=nodata) + usld = hydromt.raster.full_like(dsin[c_vn], nodata=nodata) for sl in usld["layer"]: usld.loc[dict(layer=sl)] = xr.where(dsin[self._MAPS["basins"]], 0.0, nodata) ds_out["ustorelayerdepth"] = usld # reservoir - if "ResMaxVolume" in dsin: - resvol = dsin["ResTargetFullFrac"] * dsin["ResMaxVolume"] - resvol = xr.where(dsin[self._MAPS["reslocs"]] > 0, resvol, nodata) + if self.get_config("model.reservoirs", False): + tff_vn = self.get_config("input.lateral.river.reservoir.targetfullfrac", "ResTargetFullFrac") + mv_vn = self.get_config("input.lateral.river.reservoir.maxvolume", "ResMaxVolume") + locs_vn = self.get_config("input.lateral.river.reservoir.locs", "wflow_reservoirlocs") + resvol = dsin[tff_vn] * dsin[mv_vn] + resvol = xr.where(dsin[locs_vn] > 0, resvol, nodata) resvol.raster.set_nodata(nodata) ds_out["volume_reservoir"] = resvol # lake - if "LakeAvgLevel" in dsin: - ds_out["waterlevel_lake"] = dsin["LakeAvgLevel"] + if self.get_config("model.lakes", False): + ll_vn = self.get_config("input.lateral.river.lake.waterlevel", "LakeAvgLevel") + if ll_vn in dsin: + ds_out["waterlevel_lake"] = dsin[ll_vn] # glacier - if "G_SIfrac" in dsin: - glacstore = create_constant_map( - dsin, 5500.0, nodata, dtype, maskname="basins" - ) - ds_out["glacierstore"] = glacstore + if self.get_config("model.glacier", False): + gs_vn = self.get_config("input.vertical.glacierstore", "wflow_glacierstore") + if gs_vn in dsin: + ds_out["glacierstore"] = dsin[gs_vn] + else: + glacstore = create_constant_map( + dsin, 5500.0, nodata, dtype, maskname="basins" + ) + ds_out["glacierstore"] = glacstore # Add time dimension if timestamp is None: From 947b119a6381ac06b07500d8700b39b6613d025f Mon Sep 17 00:00:00 2001 From: hboisgon Date: Fri, 14 Apr 2023 17:45:10 +0800 Subject: [PATCH 17/19] black --- hydromt_wflow/wflow.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/hydromt_wflow/wflow.py b/hydromt_wflow/wflow.py index 9b824a73..e0fee541 100644 --- a/hydromt_wflow/wflow.py +++ b/hydromt_wflow/wflow.py @@ -1852,15 +1852,12 @@ def create_constant_map(dsin, value, nodata, dtype, maskname): ds_out["satwaterdepth"] = swd # ssf - zi = np.maximum( - 0.0, dsin[st_vn] - swd / (dsin[ts_vn] - dsin[tr_vn]) - ) + zi = np.maximum(0.0, dsin[st_vn] - swd / (dsin[ts_vn] - dsin[tr_vn])) kh0 = dsin[ksh_vn] * dsin[ksv_vn] * 0.001 * (86400 / timestepsecs) ssf = (kh0 * np.maximum(0.00001, dsin[sl_vn]) / (dsin[f_vn] * 1000)) * ( np.exp(-dsin[f_vn] * 1000 * zi * 0.001) ) - ( - np.exp(-dsin[f_vn] * 1000 * dsin[st_vn]) - * np.sqrt(dsin.raster.area_grid()) + np.exp(-dsin[f_vn] * 1000 * dsin[st_vn]) * np.sqrt(dsin.raster.area_grid()) ) ssf = create_constant_map(dsin, ssf.values, nodata, dtype, maskname="basins") ds_out["ssf"] = ssf @@ -1873,16 +1870,24 @@ def create_constant_map(dsin, value, nodata, dtype, maskname): # reservoir if self.get_config("model.reservoirs", False): - tff_vn = self.get_config("input.lateral.river.reservoir.targetfullfrac", "ResTargetFullFrac") - mv_vn = self.get_config("input.lateral.river.reservoir.maxvolume", "ResMaxVolume") - locs_vn = self.get_config("input.lateral.river.reservoir.locs", "wflow_reservoirlocs") + tff_vn = self.get_config( + "input.lateral.river.reservoir.targetfullfrac", "ResTargetFullFrac" + ) + mv_vn = self.get_config( + "input.lateral.river.reservoir.maxvolume", "ResMaxVolume" + ) + locs_vn = self.get_config( + "input.lateral.river.reservoir.locs", "wflow_reservoirlocs" + ) resvol = dsin[tff_vn] * dsin[mv_vn] resvol = xr.where(dsin[locs_vn] > 0, resvol, nodata) resvol.raster.set_nodata(nodata) ds_out["volume_reservoir"] = resvol # lake if self.get_config("model.lakes", False): - ll_vn = self.get_config("input.lateral.river.lake.waterlevel", "LakeAvgLevel") + ll_vn = self.get_config( + "input.lateral.river.lake.waterlevel", "LakeAvgLevel" + ) if ll_vn in dsin: ds_out["waterlevel_lake"] = dsin[ll_vn] # glacier From baed36dafeab49d99c7bcf5e7435e69d5a006a6a Mon Sep 17 00:00:00 2001 From: hboisgon Date: Fri, 14 Apr 2023 21:49:21 +0800 Subject: [PATCH 18/19] Switch to hydromt_fews --- hydromt_wflow/wflow.py | 49 +++++++++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/hydromt_wflow/wflow.py b/hydromt_wflow/wflow.py index e0fee541..c7b520b0 100644 --- a/hydromt_wflow/wflow.py +++ b/hydromt_wflow/wflow.py @@ -24,7 +24,6 @@ from hydromt.models.model_api import Model from hydromt import flw from hydromt.io import open_mfraster -from hydromt.cf_utils import FewsUtils from . import utils, workflows, DATADIR @@ -1837,14 +1836,18 @@ def create_constant_map(dsin, value, nodata, dtype, maskname): ds_out[var] = da_param # get soil variable names from config - st_vn = self.get_config("input.vertical.soilthickness", "SoilThickness") - ts_vn = self.get_config("input.vertical.theta_s", "thetaS") - tr_vn = self.get_config("input.vertical.theta_r", "thetaR") - ksh_vn = self.get_config("input.lateral.subsurface.ksathorfrac", "KsatHorFrac") - ksv_vn = self.get_config("input.vertical.kv_0", "KsatVer") - f_vn = self.get_config("input.vertical.f", "f") - c_vn = self.get_config("input.vertical.c", "c") - sl_vn = self.get_config("input.lateral.land.slope", "Slope") + st_vn = self.get_config( + "input.vertical.soilthickness", fallback="SoilThickness" + ) + ts_vn = self.get_config("input.vertical.theta_s", fallback="thetaS") + tr_vn = self.get_config("input.vertical.theta_r", fallback="thetaR") + ksh_vn = self.get_config( + "input.lateral.subsurface.ksathorfrac", fallback="KsatHorFrac" + ) + ksv_vn = self.get_config("input.vertical.kv_0", fallback="KsatVer") + f_vn = self.get_config("input.vertical.f", fallback="f") + c_vn = self.get_config("input.vertical.c", fallback="c") + sl_vn = self.get_config("input.lateral.land.slope", fallback="Slope") # satwaterdepth swd = 0.85 * dsin[st_vn] * (dsin[ts_vn] - dsin[tr_vn]) @@ -1871,13 +1874,14 @@ def create_constant_map(dsin, value, nodata, dtype, maskname): # reservoir if self.get_config("model.reservoirs", False): tff_vn = self.get_config( - "input.lateral.river.reservoir.targetfullfrac", "ResTargetFullFrac" + "input.lateral.river.reservoir.targetfullfrac", + fallback="ResTargetFullFrac", ) mv_vn = self.get_config( - "input.lateral.river.reservoir.maxvolume", "ResMaxVolume" + "input.lateral.river.reservoir.maxvolume", fallback="ResMaxVolume" ) locs_vn = self.get_config( - "input.lateral.river.reservoir.locs", "wflow_reservoirlocs" + "input.lateral.river.reservoir.locs", fallback="wflow_reservoirlocs" ) resvol = dsin[tff_vn] * dsin[mv_vn] resvol = xr.where(dsin[locs_vn] > 0, resvol, nodata) @@ -1886,13 +1890,15 @@ def create_constant_map(dsin, value, nodata, dtype, maskname): # lake if self.get_config("model.lakes", False): ll_vn = self.get_config( - "input.lateral.river.lake.waterlevel", "LakeAvgLevel" + "input.lateral.river.lake.waterlevel", fallback="LakeAvgLevel" ) if ll_vn in dsin: ds_out["waterlevel_lake"] = dsin[ll_vn] # glacier if self.get_config("model.glacier", False): - gs_vn = self.get_config("input.vertical.glacierstore", "wflow_glacierstore") + gs_vn = self.get_config( + "input.vertical.glacierstore", fallback="wflow_glacierstore" + ) if gs_vn in dsin: ds_out["glacierstore"] = dsin[gs_vn] else: @@ -1981,6 +1987,19 @@ def write_fews( fews_binaries: str, Path, optional Path to a folder containing the FEWS binaries. Id None, assume FEWS bin folder in fews root. """ + try: + from hydromt_fews import FewsUtils + + HAS_FEWS = True + except ImportError: + HAS_FEWS = False + + if not HAS_FEWS: + self.logger.warning( + "To use write_fews function, the hydromt_fews package need to be installed. Skipping." + ) + return + if self._read: # force the function to read forcing self.read_forcing() self.logger.info(f"Setting FEWS config at {fews_root}") @@ -2119,7 +2138,7 @@ def write_fews( self.logger.info("Updating FEWS config files for Wflow") # Updating csv locs files - fews.add_locationsfiles(model_source=model_name, model_templates=wflow_template) + # fews.add_locationsfiles(model_source=model_name, model_templates=wflow_template) # updating SpatialDisplay.xml fews.add_spatialplots(model_source=model_name) From 16cbdb609ff845ce33c01363cbdf6becbd3e206c Mon Sep 17 00:00:00 2001 From: hboisgon Date: Fri, 14 Apr 2023 22:33:20 +0800 Subject: [PATCH 19/19] create_cf_link when instantiating FewsUtils --- hydromt_wflow/wflow.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/hydromt_wflow/wflow.py b/hydromt_wflow/wflow.py index c7b520b0..f9d85767 100644 --- a/hydromt_wflow/wflow.py +++ b/hydromt_wflow/wflow.py @@ -2002,8 +2002,11 @@ def write_fews( if self._read: # force the function to read forcing self.read_forcing() + self.logger.info(f"Setting FEWS config at {fews_root}") - fews = FewsUtils(fews_root, template_path=fews_template) + fews = FewsUtils( + fews_root, template_path=fews_template, fews_binaries=fews_binaries + ) # Instantiate the wflow model in fews object model_name = f"wflow.{region_name}.{model_version}" fews.add_modeldata( @@ -2014,6 +2017,7 @@ def write_fews( bounds=self.staticmaps.raster.bounds, T0=self.get_config("starttime"), ) + # Update and write wflow model components in specific FEWS folders and format self.logger.info(f"Write model data to {fews_root}") old_root = self.root @@ -2136,22 +2140,19 @@ def write_fews( # update FEWS config files for the model self.logger.info("Updating FEWS config files for Wflow") - # Updating csv locs files # fews.add_locationsfiles(model_source=model_name, model_templates=wflow_template) - # updating SpatialDisplay.xml fews.add_spatialplots(model_source=model_name) - # updating Topology.xml fews.add_topologygroups(model_source=model_name) - # updating Explorer.xml fews.update_explorer(model_source=model_name) # update global.properties fews.update_globalproperties( model_source=model_name, model_templates=wflow_template ) + # Close logger, Zip the model and state, and erase the unzipped copy self.logger.info("Zipping wflow model") wflow_root_zip = wflow_root @@ -2164,9 +2165,6 @@ def write_fews( shutil.rmtree(wflow_root) shutil.rmtree(states_root) - # create shortcut - fews.create_cf_link(fews_binaries=fews_binaries) - def read_staticmaps(self, **kwargs): """Read staticmaps""" fn_default = join(self.root, "staticmaps.nc")