From d28af3b02a0c82cf82a2136448734df3f719ed10 Mon Sep 17 00:00:00 2001 From: Thomas Kroissenbrunner Date: Mon, 27 Oct 2025 14:00:15 +0100 Subject: [PATCH 01/57] refactoring the code and cleaning up --- .../ERA5__lwt_rel_occurrence_1958-2014.png | Bin 0 -> 101971 bytes .../correlation_matrix_E-OBS_1958-2014.png | Bin 0 -> 131123 bytes .../lwt_1_ERA5__psl_mean_1958-2014.png | Bin 0 -> 111271 bytes ..._1_TaiESM1_r1i1p1f1_psl_mean_1950-2014.png | Bin 0 -> 109468 bytes .../slwt_EOBS_4_ERA5__psl_mean_1958-2014.png | Bin 0 -> 102843 bytes doc/sphinx/source/recipes/index.rst | 1 + .../source/recipes/recipe_weathertyping.rst | 114 ++ esmvaltool/config-references.yml | 13 + .../diag_scripts/weathertyping/calc_utils.py | 1074 +++++++++++++++++ .../diag_scripts/weathertyping/plot_utils.py | 408 +++++++ .../weathertyping/weathertyping.py | 315 +++++ .../diag_scripts/weathertyping/wt_utils.py | 538 +++++++++ .../recipes/recipe_weathertyping_CMIP6.yml | 310 +++++ 13 files changed, 2773 insertions(+) create mode 100755 doc/sphinx/source/recipes/figures/weathertyping/ERA5__lwt_rel_occurrence_1958-2014.png create mode 100755 doc/sphinx/source/recipes/figures/weathertyping/correlation_matrix_E-OBS_1958-2014.png create mode 100755 doc/sphinx/source/recipes/figures/weathertyping/lwt_1_ERA5__psl_mean_1958-2014.png create mode 100755 doc/sphinx/source/recipes/figures/weathertyping/lwt_1_TaiESM1_r1i1p1f1_psl_mean_1950-2014.png create mode 100755 doc/sphinx/source/recipes/figures/weathertyping/slwt_EOBS_4_ERA5__psl_mean_1958-2014.png create mode 100755 doc/sphinx/source/recipes/recipe_weathertyping.rst create mode 100644 esmvaltool/diag_scripts/weathertyping/calc_utils.py create mode 100644 esmvaltool/diag_scripts/weathertyping/plot_utils.py create mode 100644 esmvaltool/diag_scripts/weathertyping/weathertyping.py create mode 100644 esmvaltool/diag_scripts/weathertyping/wt_utils.py create mode 100644 esmvaltool/recipes/recipe_weathertyping_CMIP6.yml diff --git a/doc/sphinx/source/recipes/figures/weathertyping/ERA5__lwt_rel_occurrence_1958-2014.png b/doc/sphinx/source/recipes/figures/weathertyping/ERA5__lwt_rel_occurrence_1958-2014.png new file mode 100755 index 0000000000000000000000000000000000000000..d42fbf8784ff167dd1867d6b0f5c14ea02b9bd34 GIT binary patch literal 101971 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 literal 0 HcmV?d00001 diff --git a/doc/sphinx/source/recipes/figures/weathertyping/correlation_matrix_E-OBS_1958-2014.png b/doc/sphinx/source/recipes/figures/weathertyping/correlation_matrix_E-OBS_1958-2014.png new file mode 100755 index 0000000000000000000000000000000000000000..6288613b4ab89a2eab3d39888510ca1163656aa1 GIT binary patch literal 131123 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%0DvP8`d~=~omp-z5OwFj|IuG{T>6;T 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

;uhPk@VY6G& zb8r;eUyh9%Zf_NR`c%-&ER)kbEqIQ#)&g)MQEf_f3GF3pfS> z{$TVY$&DL7AsxOy=|?-?lX~S06wu#-Ut4*d2FjUlX5h64!iSa+R4+)nI@aFhSW4^b zECAhU#srIWmdNA_7#*Y7$cR*i+a{f31Fm|tH3ecdf`(1PFdtRmq#&T7LCvP1`E8F6 zf{4ZgNSk`UGzfOYOB#OnY6+2Aw{fDEuC*hNi9tp>KbEa=iwS`u9Chu2y4Y2jaIt|kXa<@X!FDnTUZ)zlUPV}3>fsPQ3(+uQ_C zI?9F3N{7VEY-Tq(gqrfX{Wcbqj<^dp#p&rU_Sw#Cbylvoad={NWovQ`m0*61d$JP= z=D&&I5VfLYWNg&{E)JK7WlT&XZs)i3zHm9bagQv9JMw~``7>JO)>vBW>+6qP)Y9HW z$H%uxD5tGA`_aZ)h79F<3(o{8*TjtIs7_-?$lJ+KNQ;W1Zv@7f4VO$^35{k0AJ~nF zg;y3jn#c(((bnz6@d5}-gr8Gk?%AeDNXXGzxO~On*u`}48X>Kkk?pYyI?iK%XX!@x z%;Y5SxDYp5TUx%u?>+)X4Fu0=H&!bl8DTxna&#OQp|;3)ho^raUt=eH=FbR{-@MO@ z-=Xhuf@WbG-qQAR=4?t5pWT)QRKUz;Lj{bs?qL1J+0v)-@)!=X@lxX;{wiGS>QwR1 zWWNz*H=Ue_zHJ8u0{@FA+z;qq&|>s{cl`9LeVm%MY^}Asq>SL}L#(Gq{bhgi=$=Gm zCVQT_tjDTifL<}RwB#xZ^&D4VF?5TK?IYA|lEHcdSQ_b-BqI5amL&_W^d$+xvX%S} zQoS8%sT=5ygg~8|_`?1?j?PysW@1z#R`GGaD~cD%*m!O01Xl*w(e=Gwe6)V0dbk&l zX+oTaW_E7_ac?#G$>N-LjIcYYfkA*~?a75|m&_LNwJ{HiagUB=cS6EOtFM89Zh(t= zlw0=ZJXGAqoeWq61f?c4>ohFKQ|TqKdGC(9Twx8PJDp2%_HEdj?JiTiTj8{#wJ@+e zgac&Hej%6d9SMy^2?A&;2Zht|#Y-7fVg&`f^R!jeF1u-6Uk_7k4jq@b?Zj7xN>T@2 zZFsfdTjM==AJm{o+gadb4GlCm!Sr7(oLDVc6&i^D;Ng>~82`o|oZVm=)h*#C$4MVw z$Z(kDe=(9Sjvk_}a8aExS2)!#e|@;Co|Tq1;kcHor_DQsUOXfn_(`_7=-t@U^B$15=7@Y0JRED-{R{hE5wssE1CJQ}!fi%@VS?WO%yMck{K@56jOfoTR zER0{9eOE$djWz?*AQMSHIj?A_b+|KYX0$Sv^ZT4mMbXP8j!%qRU!s;1b53BxT>i89GX8$#Paayd;M&ZV3q>c_#%{WHx^03(Ygp!3d^~^ z;}_aW*H&a!D@}TP_YrG);;`%*+_s-3pS?QbKU(>NjkVU*Np+u_jrstHqqgJe^6KV( zkpH7YXeWyWJa$|e!iCg~CBStJ@4Cv#afi`WcfdLv4CjkS3B0!9LavURL*{q+Z0~#u z5kBaY73o(|3bobJ3Zt97LKG>>G2#e=N1( zt+DoZ@r)V~ViZ^O#Pi73VTCzwmnzv|>G4ZLG-PD#C@qJgX**#-li)PwTw5gg=Zr39 zjWb+o%>#4^R+0B0Tqa*uIrCCLbjuhx9ZWi9A6;(7J7xARJm@Mt87DZ$Wj-~%*fMcn zn%8!Pq8hS`FI8?}eF_n*JEBi~0feru@2vrIvU5D@-vWchUq5slm--Osoj#3QerGg5(Ju7rsZ&H7MER1M`2Y=Sy}5ndXwwdIRd@B76Ihc zh}Rj@OA1^N?W@#6;mq2;phmV;I5eG$7CF7@OJv>PN9+5@!_B54*q}FU59!LWS3bvU zyLh9z!ohH=i55G5uZdQY-!qZu&RueD#Er^rBDZ?+ps5&A{XYZ#*SD8?o8F{oHF{&C z{EVi>F&`^kiZ?RvLeC9j;0L8Fn$ASsOk&VV!|&0j0A`*0me)}Pqiq}qp4XJ9--B}d zOJJFd7Z1FpKJIEcT*iK1J?{&Ly`ctK9xosHS~S`6`SZU3fJNk9b;n&W4}Mf4@RsYW z%!go_)V&eDs?($Ed9rc87g+^tD6grw>hx5HqcGq>`3;39>IfOg3utYZE^i zse7Wl%F6D9hDpEo@4iO-qNybUp#l3{)cLPy#6X#%bR4&1w(gQ-k}x5(QbK#I+&e#k ztJFC&5$X&zkQEtG4Gpt!T5F{j|8$ku>knT@^Lxkp`{!kdjv;Fg_Fi+XIp?~s>;7CB6TK+U z01;EmVaYSY<3vm?Cl?Xp<;PT@v2ZyvgbO?o_kI5meYz6d3`&OUQt@j1cd~4{)^i9I zZN+0whsqa|!~9Cq@?EQHPmc}7UHoULyf=BX>~6K(7b`23`a(B%xFaoG{F5cDF5S=N z(s-pRGWe3OAMYS^x<8b4hWQ_p3|3b5XUi^E24`xN$IWe;g8HpMBLHY!zNNXoEw)Hm z8Ss8;)fC$Yu`2zc-i4#j<4G3LFqYT+qr*}By&sdYrYpwx+U}AH$>OmRQa(3Oj@@-(&;q+ms!WfLr-vmb00|TOIHnPV zM$%m~jAj6u-B3v_Z}i$Rg+sh)Kx}u(b(o;HKk&*#>v=!K&(xTV_kB_?YHsF{lam99 z6-*{W#6Q051w}`ro0*x>>HLV(^STMl^nN7p9{)US0}`B`zVkIU{q$DWPhM^YM7Zcu zLixnUYLzv+Z;CkH1#!~6$BUu;QM2Wxnx1!>Y>PPsQ(A0`$7Fv+9{!uW`>T1e6!s~4 z(09MFEqAfc_5Ge>L5#?&)jhHmhs8SW%i2O98xGy+VKV?WJq$+frQfHQYR}(n67BWy zcrX4qk=;5(QJge;uLX?xjc?K!&UL+?*(Lr>TxqkMVSxm_ATqA!1A9N<0XDR-7y#0Y zdYw5=5wO(MX#$M}`t^+rjV{-}bIzZ?DsBYiMaCnWWFBtr)4==^MU?+iXP!R)r>4fQ zK@a*E4sR&z{;NYGAm%>=wKxAMsQs(6;{PJ3{ZB57HV7=gzLP@#4{7dyDO8VKBJ(xX zrG!I6yAtfbibv&iE-KA=1psjkm9}@F&b~$S*RQ`FBsP<_w&#Uk)^2V!IY^OIqn!J} zv1yg&oXED|_~0B05YBS@;#dDk%PE5W;HCW^y54`Ym%s`0@gDkb@v!jy|NU49`0>A!QZ{Isru78x;Uxd_v(BF(--2b( zb-$|DbgrKM=g*(uE(-@tzPZ;-8qQK*|KO7>OLaumZGp=|4O%3*f#^ zpd0N?FY!XSest&bCM9)86&BXtlDl3B4m&dXv~C3djE&;m4)@9m3W&xi7o5qJaCrN{ z4c=$GpyG6p6{S!~VjxFs1)^NC*;%BdM!;z+7ij4DaDn|23w?P_Dct*s=bwyrus7*P z7w`xa&s}M|OF3H&$7-_QpinA^0*!eq>+9XuC*!sbXNA{M@fRn(BiV3iJczmCLI&B) zRv<^^QUhR`p6(GZ_luODAN5HR`}g=)0WCPDTdJK_DJYtv3hj#sDK%OpM@P>CxrRig^D3=1-eDhn~k33FM2#N?vUY#Yhv&%VAnTlF zWkn#asqMYNSe`B)(h3RjL3lyV zulsr2d~$-VwoyHOsLE)iA18?m%fqAN7ZOm{K+w<890;4`1Yt03ZFL|eXYGRStugJ{ zre|PeWYG5ysh}MX3x?C{9v_G~uLJ}#gGt>2s4`sotNpob$=_Q(pROoD@wQ8z^mD&b zMKtWkuKPwQUUwRruR40z$%j*YXp|S;tS4 zf;=B~Y0-$egv$uoLhuC^)gLeSSHc5PU_5V#1Rl%d+N~F|sZZHH(R0^8h zz9jK4@G@%A{4ag;j!r;8%j@=+KZuSm)P10tnc?1@tT7m+K>y3t8a+85C&(+m?cxvD{b1WmLVo>WNG}+bzM8l5Zd4Icfo2+`RYfk8I{#3ccsf0e;JCj{Wjv!+cl4Yt-r@5l1@ z>neGlD>DBx75_D5{D0w|Qd^He!qZiD=c_#DMW z3rz@o1RfQJnG&9(mV$H;s#mX+q$C@=vxE)?#~Q5p#1k1}9d}+zDJmkRb1#5Y>!N_b z50rG4gtV&r07Tb_4CS_?@CC%B?{YfQpvp5-e{eNc8}#BKG(vmOB$J+!l{LM)+rP1~ zk=dY=j)D>oi+KK9Vc|x?L0ZxO^u4h0FT?yJxWNK?^goK*AqIX(Sp_2=NJf#A?63mu zEwyQYTZE6>1N|4b$Q^Kt$^nk{Hi95NYzrGdB)x*MKt#AtUb3U>weIF@T^bt7nxi`Y zGtmA}W&8qKRFsB>wBl$=ZH9uYV6`qe0LyMwoIxlNrHtOs=ay7p^9M34V57^0IC&cv zqp-qMX90%I;{r#!t#lR+HHAYBy%tcJE-#DsIeAL@Z$KYdw1BXBlH1XbFT0)ti6!Hj z<)Cq#$Nf|alt^zc+2r*a&=Ao7f`#gS{`8-0zt{tSC*&g|4J7b}><r&Dchi^ zG4CaM!gKT3l+~ax9)>Jxrv1D7M^3mwNGB=_XZvl79;0smJkMEcqWe(O75H|JA}&(j zTse$7C{Z)I+npSl9`8yklPhwmzAiD54N3{hcop>T*u~LaRO? z*VCO!R6?!ZgoN7>a2nxFnAq3rK}Psk5x)xpxy`)&+C(Y_p& zx6JoR)_&e#VezqP!xSixhcg*2#$h;F5y{)wr0|%awtH+Yo|jTuyI~}UU=>>@7XNU< z;TvUad#>FC+j3uhddlbCZLwkHe0AtkS7!=>FcDzk_VDloS0_s(0Z;q7VJYkuk4UR( zuQ zN$P&Fl#y`>1$l9u(;QeQC-EA9=8Q@yF1fOo8p`L6d~!MWmZrDajSXm-W&luWTWhfu z@aIqe?eg*n46UkVSAYCmZ2TMO#=w@owRIp4tygy#&Kw2?6Krg3^Te!xyR&oB+?-KO zG&<>AB(t;T4SE94Od+ePSuqZhGl? zdoI*qy}$(clG?4B?pqIl0?OoeZtCLV0*ZHVSX6S|pcyoy@o~1!0&Hyj0BEx%o8yUW zE17Cr<@11hUs-7>jV>)MZ5P05_<&GE$L*2qpN8pDAH*`J%3vU#bG1K{CzSIy47P?0 zA`CAf9Yce%2b^+phMy22Lzxw_oh1xB&S=z2d^|kNBRmFvlFAUf(RWI%HAIl*jg63P z(^y_HF`a-0XZQS#uv{H(ZbNLH(Mkhr5Diy{0$BgUSrXCw5J2S?xmnU;`yJg~N2nY;>hh$Y$=vB4o^78pEdE-3^I6mK=EU}--5)w4X#_zdaLChlU z#ehV#w#49(ynytDhxt`CIM;n~1Qv<%WCCgHB?L&E>N}-fri)YIC|+GBWo%iCkOHb` zHIKV`{G3?y@oi1b9Ki+A3M~$1J~+tZwTPn|H6at^Po>Z&&W-s6v8lMk0{f(TE8o1= zCr`9$-A8rwv~<{H6(zLd{0ajot*_N`Kq6o(n>Ot59MRUzx=2Cq`ZDcG}Z3EFN_%4h}n5#jfNy?DJT)wl=G| zRoVC%b^|0N{PD8XF^dbXCza!4h@oNkllPNOQvo3)2n{Fw(@`6(m(6tf>AK4$-SURY z(TE9yu4v{~1{~!4;#fhtlTGmn^cD6WO*GqyoVUi&zQeSd9X~x~5p;CK{HvcnUtH{m z!@Iemc%}9_zCR{bQTHfYwYA;l1Qq07#{Rr9~MP<8cU6iD3L*-;r3?Hd^I z_4lvQyqoSHKmpA~?G}oPre(H|7OUGK69MoL!Tz0bQ-DK%jfOVD%G42JHOKUp2Kj@I z_C%P(xlM!DgPSF2vOU`x{tH6ZH!||?=nw=CwY(>Mf8*43L9GK^kShh5Ucw;D^<+xR z1{jzTC#U$F4~e}VZ%i61t&2rVUOFbr1}!h^pYP9L4GqOd-rQd7Wmi;0pRDzAn6~V9 z3MUbTz0<@E3Id`HHNA}6Jw6}d@+T{EO|CNzMtN25N-wAP1(8&i_Zp%$q$?BA)_t!p`nJK_7m+5hx|)SK>-r~RPS@_W-Nvx{iI|{);QlW zgZbDr_)};;^%Q&D0)&McFf1veu(C>S^w!NU7E-rxS`&}4vKo{%>Rh~z-KM0KdENWb z>|JS@g`A)JIa6?CB+Sc~86L3Zd{Ic%)yV61$of`RnpWu`v3PYAif&+Gp*z~f$>r(m z_0T3FEzSO2c1On|VirxyYCuQ3yJPSAgkp*QYAxFEQ%yO`xAgQEtgNFM8tUqTG-Zn! zIxQ|R1Ox=z6Tfhx$pkvG^N?hEf zxbn|3V-+vXmU1PwM{@$*?i7fi?A)|6O_6ev7)W=t6(Lv|sj1sSJM@f154WLL(@IWq zXoDNx@+b~xyBCbygp3FXVYgSd*MB#;Dw1b^|~` z%;?wGH`HJa%G~hhvu|o@x?c82ul)4uWj>6$8%vq=2p+v=KwjQ!IyF;uM(swQ%1R~x zOBIojC=goHAS9xc@WTB~L*o};We2UMHyJjxj2}I_?f+fIE0mh7qARPaV{BfWMg{{C zEmn1PzWmPM!(e7U5|Tiyti^_T{Zc8fU`tiYd9J>Fr0Gm}{>KG=S8%gh3#hTl@6X>f z^fUkdO&^<>NV)V#r>;(|)>&JtT&i)#wdh`P+F6&EhcA=Z7X5U;)n&d{@`hFEb&^k` z@Ni$D_S<+>-AF(ROoD?J*WG7>{l&7#gTJ9i8>eT`5qYW5Yb|b`@a+D) z*LC8<0|Nb${6yp3qBjSwuB)r6cFF1KeIGzWgnH9UeD+fN2OUwhRdslCues@@gx{NNlGLHMN|B$pUn(>iEpBeH`Y6(tIps$X#Ei>?dRBQEfJNW$CwM83y2lL4g=c|-&9a-e$yxR9qQo%1UptavO)XGEL zJrfz#yc|1{m?_ZjkGp2CM#sipHZ|q&f$ZjDC&k#5l(2jmf!?og^i)finjG+wQd4t9 z5KTTA8!O)pYO2uRHz8Ynd3ww_D3*#QH4JM`i5v0`4D8=(XEk9`{jJrsoA*atOXFR` zXj4=C#nH5$7@~$@&bGoryI#b10T1<~kpx`azSt)|i&F2$#}E)CO@DA;xBaKet@oXp z0;KPbU;k5{(~?w~@i4oPY=o$8FZ4~CXpaB6<7C^dlpfi0^oH&W^W(!EhWrvPb13C4N3;7C!`Rr^NP&E2*fi>k z7b`0(zO3e`mw=O=3jz{nZ|p&f(Drl%S&Kyyk~G_R3cTmZDwNaXnyKD$29$^H!lvZ` z83Y6tbPW%qmzS5@e)$4zb-$_vbL)BHRn}CZdyO%t(-s2w+jk>#)K7RGC{YiY} z@u87E-f&^5n5tMsr(Kted^$fo2~&H#x?Bg!A|k>}f{l?Y$To;wq-45nb9|;1)z^n| ze1+FK0|Qge>80&Do1bm4(Wj}Z%5U!wPUEF!;>H7U+~1BGFMvXSFL{&smso6QPu$;Y zdt6N3CGmT{di}LO)*jw}bxu9;mi`%IS^wfnLdDxix zo3vtrRx9gwRp9fJqrAAp&DGq%mN@(2jHgjmQ-gn@StmW8dNN}|6DTEzYxAtT{j?dz zTwC?AfQ}lY!5JG@_3hO{bNHS|y!bE-fLCYP=$$;ZdBQ-s%Q(xRT_Q~pyc_3E5Zj}9 zf?l~a2!xo&4wes_`w2L}_R^Y)-pOVdHqyU8XSlzAi9wKz=IyOG3=5mG43@xA23A(S zfMDm&JSi!cbIFGS<-nkx&S4ui3Q9&Wor;kbP)3op7u9Bf)7e!I*0I=BEqEm*k?q|u zgrVL7J`ge4HrzBH^kVh$iBclNxKD~Ua}lM z9#ARz;ap(}oGbRs&WCr`=i`Bu3DNs?X5+X71UsNSDsFCW4mHi~w_YsW$G88YLQ3CI zhtr@hn@;0aicR)oq%3vzDihA9xs@;K7wuBjTtmG0fQ?N4_2aq}{J}!_TynCy^J(5} zrrz%&rFit1?Ipd}OB?-I&Vsc<8+UDnfl0Ss8(~S0y7GqrWB1nkvajkJ)u@O;P{hXv zR#yK|VF@wMg=RI^V82|W4Tn#J!ltJD_xCKA1RC((-lHfO82TXq23^uyqNb(a8zme+ z&A7F@8>Lhr>dEPPivPn=k-wc$dR@X#C7JYeYe~69Y z!-Krl{`KBt9|g60O2{7qkwQdUJ^>!{n!3R9HhwzI`ciyPs#^|VSQ3_#iyDhIB)WPH z1P+J~sJPLzT7#{kL^SE-c>*E5Rz2!(K*^QvMknPB0ip6OAYg#T8_i6}kKx;|O4Csa zQIK0WkU(!r-%iysUun>F4Ada~qs3Mn0VgM?=g7#N=mPi0%2hg6Rt6U4VWXhyK+}!; zey*X{Nfe=tgflZ&Dt#C`a6?((}}|Z zm~+eTi$Q{u?%iksT&2}=9k~4i-#`>7-?p{&>WsEe<6HN$53yK9w@f<$0X&i);wc!c z(X@3^DJg^_YD+a(5g<%n9*o`HEy0u$i@|ku1ZbqZ=nz}`>aJXgf95*J2|)h+kn+cH zC~Z_yt}SS>_2J~8VRfgc{l5CeyLZ3TTiuuejE8tAod-Nr43WRxgrJekMxqN42e-O6 z6cU%D)R>6L_hgG026!ClGS4H2+~F%J`4&<$2fAMDx&=~8OA4Bq(`a@>e~wJzaA9Ir zD!Q0w&K$2yes7qw{sRd=Su<$;g!CFWNIS&f$!B}3eJTsFd7az=w*7;0V}wTJs5g}i2?L;X{WBq@VM=_K&* z`g@Cf>RY!2V7h-d_;5#ImcpySE4y>=$~=Y?qGelF+FiYB7+WdeLA^V?{%e)s%SJRI zKAZG94S4K%)GO!~tvV2RqT|NS$?>9tQi^h*W+Oqk<3q?|F#YQ`pZEHnRE!*`;!gs#Dp0K%;7VhNB`)>S-!I}_^<132QG~Osq^0i)f z^0&swQN+o}78Xjxye>#2+<%tWf6atMribC_Zi98)Jd;^r+yIUSeF2;flY)o)n(aO= z2XRNs$MbJVL;Q9*E{#oXZPc9tkJK5*x@s4H_qpus^+`+y@5}YtgM|F=t8BAPhda9( zRt^_Fd7MBE;Sr*`etn}_Z!ma9L2b$lcEW)(HIps)P3~%OHCKyRRKBbxG?`bGpTk|G zSDlwakrll@R@UM0y|}>7`BGQoUzSWDel-7X$XiY>iLpZa6%5Ru<9^z|-=?&&%OvRN zkPeApG=rmjnd{AAfl2w<;Y_?auj4FE!Mm&QofkeyW5AHg+4$~VJV|N$J;kAt?U!d6 zhQ?nlg3&7L>U*{cSVbSsPJZ5laG{W$gEiydoqv*9U;Xa)t5`jg+1>dGXQ$p1i8Z{? z;?Z=v64cYPS~)ut8jLr!WpE9NiBa&Im|*FKN-ggeM~6JOTN+7W%c&8e82yJQew5ar9#f+~Ri>i#=Cp-?Y@Q&0e-Y>&E-o&Um$1Ck)8hlP*S(8u>5hS% zJjke%Z_3{#@HQ9E?V0;=u^=Al{NQ$hTp<;uGqLCCtvO9(FPDQ%2w2YfZSscY1?Y&y z0?)3@JnBj^)LJi8rmw!RF{K+93$V2t+>aHG5)%_%J&q3S4xf6{1l4?hZ!HKlp?M!9 z&u=#G^5Xb6;_kJf1KeV2gDZ zJ7bz_)&Oa?KQI6FOQ?#T#>eX7qoUAyqRFDclW7j<8T_l4PL=DR7#SJ0oR7)i?0Etm z(sZg+1NyKIa)o-saA+%mAYN-ZQ@yI|q@<*jl$`u?eqMV=tEoRx>h)Liif`2>8Ch6> z(SnINS_7(}Ho29R7$|rQ0m?Lisi{Ql*7Jq$$1=Wth1FT;?@a{V79>z8G>KW{rhXAv zRtcvj*d*Arm2aw2f9;f9&|Gaeya%hoAJ?8Ij@lJKCHjD(PFahqWa{H@5*ARd`G zT{!CVQ_^z6WvQc1aqYLG>HO(Vsduj8nojGjeQpoj?0fqb4e-iw`E>A_7S~8Zv_RuX z_qNu*FJxF~<}l|?XWjLlJ|4ebdU_p6I82{qH!$!Z9*N-i98j{ISa1zcq{r+`Q zMDv~{{d=r1&3sMxp`KtC2#JaU5oH|umX;A<0x%j%;{umbsk#Yx+ka|IqQ~w)bW|nC z{q0%Q{^um$s?oSl5eYuQg3C<6ZWSJ6F8PBxSx#*&c6N65-<=Dnh8dWfYgXxkun|mR zV(pV=UtjPH+JKJaTU0;=h)s4JOc-G0g!n*5vMXJW_#i&n{&1x9dQpR9-lB7!cjUD1 zVEX%QO$PDfK*S54?Y2Uel@>WA-1QDsR&9SP9UKB=;Rj51_UVgSL|rceCMJknCc@*r z(t#%`5&=Yh7*|qmNLYW_3mZ?3odFJ!d%0%#78YW?IN04CdZ(!RR>bi<_4wG-x60^h zW%;Ltc~4DHK>;T4MPcA4A>3qQLqK|Tv`+Ir@chA8T7Hd9r7cmPDoMnAysvpS_}k`P zt8VJ{c7CJt5nX9%iqW4+{p}qBSO|z0t#a5x&CA#64JPa98W}-_U=Yuf{7!X-fuItc zBBii7C|WEf1O=|_PnMWk8~FpSu0AzmaPM#iv^bpy22!@af4nhK6tlT$*srE1q^E|* z{o}{ATMz2{ntD~*3=zrabi8~ZIx^yQ;Hep(BeiD#KtYv1+#xt|z3!JCTy&_Atff38 zWb65kd`!xTu!26W*YGdp<=3gG*8GGmb&QM}59X0PQBdM=a8~B`Dsnj~a~JJ|h4+_t z%>35Z!CJjo@lhAG(n5{aare`6qSU-F``Gx6i1sH22t+pX5rW5nU5s5KFZq0*k1sYo zEkeZCm--tbYi*@qN4*tOWu@Tk&NwHx2^vJ-#qoWLtIM*tQSa&|pgSDROOU}F9(Fw3 zbp(^;7;b?g{^tRo^-o!)r~un%ItqJzYf&}dFHM)qtC7#-umx*vO))4zCbVNySb*s9 z$OY1bY)^8oz0Wo~)bwDuxdnw_nB3W8(qC9E&`dGXKXW>Oy*w!4agvlI=jBTW?b1OV z9X22?>!Ve=>zVlKq+09p$_c+_L%optAIt6SZF0s644bwaw!AUz_)Dd3A~gBodT`AM z>Myf{Yot5ZxH_Su#ET3_AYE!5X{unYUAT= ziHqm4zM}H!sSlsKkFe_~$9wEvyJ1rDcT#npT5TTh7_>Q2-0wEk8=3GC5ON-P-QMGZ z$)C^h%a`TVF(goRMuccLCj@K^B<6tGRwSq;AeM4Tfj%ZpO7i(RxJDAvB3e2G^Zm?^ zZ}*townzl^$Zi}qv(DG0ww(@9$H8XD$OtTwIVX-g)*VShM1`}oo48H&$KR~-O1RByp=bXS|WlKMuoG+stj>BH3yH`r8i*Zfgj6<{3|#0RbI@ z<5*(9eo3lQGbV@xcgpGNMi3Jp+uqpcdEU`OI%?Gz8v)y7C?z$QPVK@eBBYRg*dy!? z*ua2ocuxiGR&(bI{l$c2S$8=Gh%etv36 z*?^6{Se$|CtE1iz0s`~rXMg!9)t3h%X5t9e*VoJ6D;QbWZGyfi#JIs|)x!h*Q>DZQ z5xpCyj)IahHip*iL`;7G7bN5C#sfi@!@sqaz{8`Sg82kfJZe9$mY4mK{r2*O$WogD z^}Bce4bHTXj_Z@VliwUvyO_fmmzQqGCsIO&hT!P;N}3s8KO*ZbVR!ec$rle8qg?jWa+;X9*;I)mrn7^7D-EA4r(&R#j!w8dVN}>aqDD z@3S$5$B7|loq6CBv{#Mp%=QcP@{LTT=dYLBdpj$#A77RZ4j!*{2L1uPoZ{EFhSMuu z=1^7#=iuyCzM-3Iaz%uJkF~pi>>BR zrB5q|Xq4{mX~!o*)`k@OIEHhm1b%5Kc(!BHc<4_1f@gMXyCl5}3(qevZ;wx!BD=eV zfy`Z0%ljrDzOAhRldQsH#ce|x^Zx$p%2r~jy}k9`AsVZ@`~K;PkeeX|H}$?!qVp0o z((|se+1q!fKA&TMx zXXm6T>2Rs-uCh>Bri~4HQYst~JH*T^r!UUI4yL-gn*|Oo>x)HfQfdk; z5eMwnFdQjq{NB~>o3NN-20gj%wO%v8b|Q|7lDj@==)WBQRRjkB6+s_T>qThdfk@&2 z@rkERTq~(i)dw+j=2Tm5hle9o4H8k&=>0FT46+oS98)-gS1Jwq2$&#T54YK1;(_=` z*)rLSM+!$(RLuTzy}5b#9*vG{!PSoUE^W!CfA}aFMAL`<*a9d!MNEG`pHvQCU%2>0 z2?;omFp-u9*Fj_h0XafY*yY1=fP~PwzFsG1uEM62ph4Hn%ZvLt&4GAw((eyh>)l#O zwNVxrPH1+Z!qm_(OXA}pzPyx^lq{rUR8_4{Vm6|kt#hbwp$DNbY84qBAdfY|ZY^PT zvB1LSZv$RSd=>;$LGgAkYA)B8pPdiMyFgN&mlySX z6ILLYUu5SO4D@ZHOHTKMLWNsOAMWga52H`t-z&t#!50aynQ|B*oXVwtq{YQl)X8x5 z(^YYDVuy6}_e<|w0CgTDtfIicz+A6?lRpNV{(`8_yBmRN+oqTtG|0w_lZ*@(0HO&b z>@ZN$;amD_c&;%#_VO7XLm^(Jl@`dLJ9!#N^mRIjOT0QjAy#X2sjbKD(lF`?!D>!V zcLS-5u64qW=N}+~$`co1@GWVpraz*h@Wl{)CMJmMj6z++L!yQfdk28#Ts3xj??F&q zT^#-P;?OFg_H#-k!dEW6uAXEiyjSgF~P;m8N;S*^oyUUCBkXX*XM?ggL z2>SZ+g5l%P>o?iaElT+n%{)VKWHMQXuXWyj6MFkCJ3gJ{{QNwtpa2ol_iH%VVQg`?#K=@K7_ei>S4Y&tco9%)1yk@hCPnSos?{Ey|2o2XjOe?Dh1j74^Zfu~E`h zDekT-^&Xkl5@Oj+o%|YH+!XY$U?9iG=VrD0w8ru4`QM9M<#GPEYL9+Z~~$q*kl9TrY2VKvb2bFt@p)QHB`n9&fm(5qA%kO^0p3q=YHwz)=T5ln(<=) zm-O$K-qywsM#S#w@E`SmEqY78KaPC9#Z~7*A9CK>qCipM4OLVeQ7iOD@JAF;Reyda z60A>%i;$EoH2o8X8v%i7uKvX)IO2<9g-G4*&4O~|S+lk0(9yyV6{no16NwA7YZy3D zn}Jjt)waz;1~d=d47Vz9IqtmT){Hn77mM$h({dI5<{K3?bWvY?&^OHSXk%k%_fJlS&(DYU4G;S@HM!OFqPDasycX~ZR8(YA;PeVQ zJ*D7se4b!nFv6@v`kaZ79rf>>IoQ$?Lm!=}(tDvFd+%W})y#1^oGxM}_fVd={Z#Ui z{xRDd8HkIYSqS|6{YPs6E`otiS%^!qF5Ng$1{sg{aI}mUzo5TboCMG72BQH-O zlzfk>`Ziv;B50@gANhOb(K(kvXM~@h^h3c1k;i1#$*GF?(9k4#mJ2JP|}}0gsT6W((fYvxbS(-Rue>>CO+_gUyb< zS2t&-%wD{tyimzjYR1((iui=iSxx!;D=sb_iFU7s*smY`-YKONo|96B!i$}H(!@y{ zlvc{`Br;^H00G%&3J$Te5XN}BW9%cU3LFKc>iyu^7WDbtpDLedbPyW7!Gegmd}4Hx z;TBLd)_^1lpM@p05a64N^RT(ubKU_H*-PlhSv`W8rQ<-I*?B5zpep#v+ud(I!_HDe<1PTg} zV%#NtB#0~yG1pTb{q$c0S*p!ErY-5$;+}^C1qQD1_x1r(iH|o zH_$n1aeaSQ0n%n2sw`&`&7tG5lH}i}1(KDKbpme-=1K`1U%y;OB@Fl~@aQvIYc^sw zh3b~hL+QM)zTW!8FAG#s+Kde0yP~0+1w}6}Mun?;YlCdsys+N__U**fIgvWgH$B7Rvg@q?gy|IIG~!RinH)csr% zr$78)Fj-QoPP&X!RJ3@c7~T`;kCM{Tou@QZt9ZIDFl}vZwoXpspjuT0>^1X0zMx1% z5xW+<j{C5wtN^Y$f@v^jMqJEg>GtndvyBvmmt=50G!Tbnfac?CwZ z`YWdaB&-;__2-YO5zWL7=5O6V4#fPEdW8m#2C&v$*o_3FX zv12|4l0V+QPZBa2LFf;7gfRCWm<=YC3Yq+|{_kC#IrgmQ7r>6`n=eBvG25S*&in2b zA@kGazClRnjkedls6?))VbHnbhFpn8jc!k|%H+|)^z^id%k^Cns|h6w>t36U!{z>C zNNDmqo8bs>tHoyMKD5r&r;Bb4Wv)$?Iiivs1`Q7Krg6td?C-;Bwf z5^AO);;!m^gMFZpEq$Qj)rS9o(EU#61b00pfzO4U^uzI#mf?FoDv8qZ(qtDM5?tJb zzSyh%PbMZRsdbmjLNvm{{Joulz!`9~JNs;Na}&VS#ErkZ4X0BN^!Y@!Jnl%ordBHS z{+ygl9U2M)i6iDmza+^kn6~+(4Z8FasHrhF z-UtYweE(jTX``f6v39rsqAmmo9X9tn%XKnKaO73KQ0jW9d75Aqu zHo?Plg1R)1^d14BYSi=3pK&NS8t^YL2~(a4v@45>Z+0y$iINV(_eM{*{3zgJL9h= z%z;jDN8^$Y{dXwDlf&>^5iZ6veO$k{B_O3l-f647QbK7)APQU}BM)R;i` zHd5_wYpuX;*VmyRHf4&{H)*H{$nwFNb zwid0|Rng8M7>q{GE1d=t~%F}N>FE}$ zzWgrW%v}TQEy0tjuI}p7;-b%e0s*8yeGix~;EwiD0bc0F}%FE zLL%sV+uQ$a`anF11sBa@TY zxV^ny3SRdSu&@fHw)bJtWWl~IE!)mVQDa2$2ZEk|uPbX0Ub^)YI*TS!y03|Nz!j^G zitlsT{)h~`6eGrK;&n!2Pin(%ggga}PeiBREW5KfQB_s8oF4ceOa?o0V=yjY7n`Zd z-3mTniI8p$!Q_gEeqLGW`0-_RYm{>N*uYLe+mepyGcuDGuR?U*t5+*9AC(+1@i@@R z%h`cqtn^{J6cfFaP@s+0(6DEIf#MCHVGQ2 z`Qkjz=4Sn$TC?}0d_NG>)V%kX7{wjV&hnq5q=q1(GZ@`nN;SH%;qL4-fW3g^-{;5a za%ktcgJ1vMGe|iaT4lT>)9tvt-NB*GwY!@k@U26-&oAvVJ%g&wwnS3C!7Y*U#l)pW zFrRH1&HFw31E7vsiF&HEy4J$5fCN5D&w%#5yxavF6NU=Z$yq%(N!5}Ut@qahRwbep zayT`$&}Wd4sAWGu&+P1&9KJyPL!LMG8!drHmOp^88=ZvD3A~0{=5l;0pm!u>V&Y{t zv@Wou1e1cITkIMLyx-*F64`AXSbH8H%9vd$3Ai1Zx_^B`3yHJ}$J1p3lG&sLxB1tE z7@zqGNl5=w7s+}S#;rQL%q?-aGi$(g2VaS0^sfT_GB0Vb0$O)6cnI83=9lgq2a!aDoH=nVr=1xiU>l& zQG!Y=EJQ``VIb-?#>f!;`)dVMf@+9J5FKDDUR61o!Mk19|J)vZz`*B(Ff(Tw^+aUT z99al=>HuQ_C4)7twhjmhizSb~3gTTn0}FmArG#?}4Sx~{0189@T0Ktxhf@}H_T z`oYwzaKe+Y^^KbR7QTVl-kN6$y8K#wb2XdzynnE1)ucz;pFB|szEao>lk%UODJemz zT@+#@Yq7APc%G0|yEh35h8j|#eLE0l2g6QyJ7g|z-Pfmk)8T6_hiROP#n_oqBs!zD zMf8hq7ND29F~}G0q3TnpsX&9xQc$z=x08*;@RQ7^h%OLx1)2yz@fq%9h-VEnJ=ZW9 z_1w`sk-%iVe+5%Lp`6qU6`G#Jfns7Z#ZR}7ooy+Cu=cYge0-2a(+Se7q-7KX#R2_VV$<0xWsSYlkgx_{(3KIt_CT?#q& zR#J3z?p`mfBw@~!oNYKds_lHMCefmbk~=W3h0opdlFM9?Del!AaC*hn_s zSZzpag9er9U{s;UCI?BcJ^DJMmzaE^QL=Bx_gf0N0c#e5h(_E2phPQKw$bH*fzKi$ zBV$uj!&ORD|Js7<>+6AzMk*{KV)yjuDJm|mIxnJ@c*dR5Do|2fUSslCDINJ9Nr;ZW zP&n#!uh@FmkU8})DR#6N9eb>@z8>fNs)XBn8{)dSo#kC?HtvfTE6t=^gOWj;?xF3#3y5Nt89tRX4>*n+%lDrQ&9KK{pB}|Hvdq8~i{=-}MBMlo^wiuH~zHI>6K!ik;oT_{P zZO8b`hVY=M5)&5}0BS}QJUm3;;~k%$i<+7i_WuILrN1Ki_=>XqSPKx%^%Sp*i&OB4 zh>QS{BeIt5-vaM&4{JN(zqIa(`QZc40hbq!Ry@$$`X+<@hhnn}WXMzr^7$ ztJuzCyixxx@xr{_{(q~+zyQMG^Z4vPG~q`!%I&I1Vn)XIwTf$=w?(CjYxKI>BZVhZ zrDhYhOqt_lifdf&&EJ!^#jd%!eleeL;qqoT=S*vHtx;Tilg>9?c;Yx;4@Qc-ICkwb zn|WBjT^fec%qCoVqh$!$GOo&w#Uq0=y{#u~f3FfnFT&iwPlIY=8T2NxN8pADaF?`oHw<{~w?EAEkWQ1OBhx?Q$`xHVRT`TwGlZ z`s3a;+JQCu0=So=fKfdg(Dk=LeyOhe0o#20QsdHr^uJDE-aGK>2f$GNYgzx{bPxuz z%L9UfD5$BQ1A|OJQPDdHITsf}u0(WCNkv;20jqIGXD94*g&wFuD*9W?9K~R~KRXw7 zvS;x^M-|nuV}^8ebizK))&B(lm=(ycU8iwfM2)_E4R4&0K`kxU6WJlyx6b@pBsk~B z{RjpE)T)?j4=z#wb(iIK7Ek>=kY!;R(YvzZ(rBG7Z3heMqgv)OD64;@o29(CQc!{d zQm53l;I59~+8=3j$$0UB#@aUkcexLPjN%Ie=X1E=d$#5K^iXl>^6J&?@%e<9eKk<* zpfN|d5J7o#&`t$cYg6GrUNFF9p^602zp?iOOsDYP=+8+^+4wUv9jnk=z@P;V)x=eQ zbm=%l*<@J9U3gxP=kSPdZ{EabKCJ{B-c{&6YkN=yK6>osJ(Qa**rq1Ib}yi(_{bs< zbCP>`c=$9dA)KE1IxiM^3S1rOh4DBK2~(IwP3xirwY9y1ERW`XvRvSXLnqVi^?P>0 z#KHmx$@~5mm|pUM_7%{4Cv$x2mU{_N`2!@(iE%9GuV0NN3c7SbXn~O}E%@A=%cG|o zefK94wF?dC)n8u%x!JiVLP$zFO!LG;M0jL=982TZuWOZTG<;3FCx0nus{W`wGXG$*(MCV7Oa$12gg## zw4OJ~@flXH^-oy4eb6vQKurtR_DI<1^r4i;>?<`M0z$|6j!!EehU4vfOYSe7_C!S3 zTbrugL>Mpqa8DoR^gcfY{kmejI5)d5SLoHc+zkx%ehjS`{2k9PgHwjHd-t2>qBU9A z)0@2Fh_AXj=I&|<;)XV=}}#l-@~!Q5W|o;&8>0T&6p)eYIV?C3`ZpaRN2 zb9>AE@hq|#+IhnJ<;Ul>;`a8WuyFF(jq0jZmVFrRfTb-F7ym0Jh8%jngJe28fpK@~ znw=*aRLw%r6TuZ47YD2Il=mFT3`t&|^)(gVE2iW6kGv8RBKxx&A#&*v21X8OO--Lo z7iJ*_1_uzxhm`G=o=A!ae8txBCT)H4<$d6+2(2$^@!H;1P1xF-_9gRzSX%nb)WDFC zjEZ|d)D;pL8-q@CA5qx(W<-yt*8XNSgPp6Twf%e{uIZ&O`(=<+N_H)gmA# zcc?&fgUsSrx7{xjodh47{Xag`BMt`>q+U{j@P4e6IMemCIW;>!gf~6ij_>L@GF4SB zj|IwC`7Yh>`8fzo*QY2q{7C}R>Z^<-JXh%+x3;LYL35UCb+slHLl}9b4Hn`AR3Ij_ zD<5+*`V*fUdw&kPYEwM(YVA0L3=m%m=`YZ6cve;dct-1u`ycE~p&jGp%kMU}?$|uL z5zZc?EMaK42Wtl#&>~cnvmH=hZ=AU3(cFYU$SEaWI&34l$jB#Mcg&K0DwMpBckthq z${!by)6o$yF)_IVvJBb`WS55vXP1{l05L5%>NHwa(>yrESmWUynfwYbJBo!f&JpVxnTB~IPzkguw1nj5DWNsWvE&3Lql|Ii??tZF0EB2b50_ zcOEGwb3NF~e;jM@`4!rNPxSePB!Yo#>`I%UmYO$}m7~2s2C!IIQtwmS&ra6}U3cVw z%vz<2VavI-!Q5N>AGj~y*10mWFjaTQ$TS;VksZI~x4(XU=XB%d;EI4|ombNJRzdVW_Eh zcQg_)F->f3%eH;%en(93h%-5uJd&u43%&6fZ_u zEv-=@3PTW70#BqM}V8?t0eh*LG{&9x79_dw#rh2rf)U-Y5U` z)V21I=jG07bHwpsb7RX{Zi=O<_E6e;Am3BiaP^_qG;i0kTQtY^ac~M#ky`HhIgKK< z+9I20$*5nf{Wors)}#2|Xje3yQtV7O4^fWCbA1FHLTYh#z)gAicQ~2F!Tytxc{=B6nw)n0NxsegY$96WV`g;4( z+I_(nFUE4qQ<#(Sq5XblE*jR*nb3>uDj4_$)b+0b{DSqF^BP5S?>fiPQ4#WckK)SY z>;0Bq=jUe=`LTe;p~~Sdw7vrwYs^A~B-yHVdw4oIpD@kdx|h!$s&QJs;PBh)#xp z(3RDV>xrmruVZ;(_X=lf4Q%@Y}>@glH6>u^t<& z1>*9O&tr7=lqicc-v$yJLB*VurQ301vtRak`J#DvFo$ntrA<;71A`+V&~XsL9@_r( z9<6SS;yEHF-+OKWw4%7-4ztEUDLGq9>4PNio2GfiDQ~z$SqO<*JIF7Iaw){%E?Yi7 z+@Tc~+r>guRc*BvJ~8Epqoqr11VoMbaeKsiYI!-$cxCU$gYDa>y`Qd=w<%t^x?H{s z5UY%IgU2#1ebMpp=<&+dFV7P8UN31u>wNdwV!wE5n@@9-p~2GU{ZM3g?~2EaV8W>k z_33&Do>U^Eun^ZcdE~T+yzaF2hDP94JE`kUWrhfw1b2x!lKGMb7lQ=VyqMUN7}E3m zDb>nM_TA+cl#usFS2RhdUgyjodtbh}*`&1pUMC`0I-lck_g1}EE?l6ZHoS6alAZ*L zyDAUD3A}C_n-Fa4YTx%5i5_uAT(_8JWH2=BbK168%n=!T&dQGOF;5-q0ix{RI%9*4 z7c$arBoM-10AK<{L4%JhF*=3Bl=ANDm}Lj{+Pdu}O+CHoR?BSVc=f^EwMj=l<6uc7 zGL+9sb26h;Pfv-I;rQ0cSCX04!U=h|?++vu?#p67(&3Vkmrp=ecxPr}@#y5}2_|3c zCG6|d+C)7nc#3oJw^RdPitmrlBkDv%<(fQgZDIV8pA(= z`L);LV$_I#R@jmd(lVRmpj`86-6(r>Y6B&EC2$do%d=@Qe`#=vf%5D>p^Pnh!H?gi zwmj#WCTB}mohzm5Can+Dp$G>05eUBpwkg*t&tt;s=cL~=igXXrV-=mhAYF^Es1)0K z*WBE>FDvYtf%Ujk+fcq&KIQu7$(qH1dI?})h$iou_@G)+;C`hpJ(S=4MGdCaxqudt z>Hy)Cn21&#%6Tm{B04&7B+d7k`2M|WMOI%zB*oh)a*FskW=<)lVG|PpkCo^O z>+>;{3Guwxt7GiE>*LhIp0%6KmX>_FW}j*L+^kHq9}A1|@=m}ybAv6WdW~00qgYyB zPmk~_8AV6e{SU*C>ergez27UHKy~^>%I`6#1VIiOcTTV}pakyAN zqTb^WIoB3O!*6y00uO)k1&tk|#(T`SqmI(+?{WB*G^zE5-jDy1=jDaCl@FcG*P;eF z{eRcQLxH*EKtPer`Udjs4LCm`X!tF0qI;6;JytZYG@MF~@#q&lx=2nQ^%jBFCy(wl zZflF}A|PlfE$46ic)Rx7;gtTvei-ik{3%zQc!7&6 zCL$@*c-zq0Jhztd5*fLNhdr>N;3&RkEy@;kE}A}(sD6&WI)6A~GjofkYj=G=;ikRC zP#&9?8TVk}I>N5!6Mp>Rwv4?30YnlioO(_+Jf9b0Cz}Q-`Z?r*pINh3*iEp%tki&8 z>Vk&NbX!h@WYc?nFWKx(xm04vg_V}xA>)eew8&W(g^kTxyl(t+o0571h7fk-_mq*= z-Rbz5%yGYK8%u55cHN3>Y-_uKkDngy^aB~Rhmul!J83JV*U!uWyiQwC9*+1X~Ydzs3fsgX#r*myQiUQ?5-TE&v?n*x-TSC3EJ zT)6s@x%CKZH~e$xmpXbr`Fs#hE4eNdP_BSGGPP({3odT!v!Z5~Snp7#(rNf{ka86OfJP`6=SXSlAYE*8)4#^V@>4hN6lPU&$>So7L|bRNNQs_c!r7 zjBZ?EJ@Kg(9}>$$T&hV;MFv;XbHErW{gY&K5{-bus{j7<&3KuBbj-E z5gWzgw{b4<1Sbkn4>WdaX(GB4v7}D$jy6N8oF5YTli>$b7Kso+tU0{BYzDOAdT{R# z=Gy1pOS_b;e8DM}b!eitQK-whX%18*#B0F&N%zBGux zUi*vK-NR#JKv_yabEOG7pF4TA!>*7~K%+R<{i9i2P3N`Hrvb!i!fd!M+bi2VpE|sg z#WCBE%#UUvG5lf>$XvA?BqVf5I1*%j{o2uhB0Ufkg!TR&QQF$;oXmeDCdox*u)$MZUi>GKe3kS~BJJ z_E1q5cvl6+DwiWJy{}v~4pA1&>5S7`>}-DZ+^KPG64KP}%XswG}rTAs`NX?HQ{BRCvsYsV$&NiG~Qqf6D9-cEiv{rcLVGlH^R*yH;HI9ugY_V#}!6xr-d z24q7%hu`7<1K7}`^q;_nrF(QHy;Lld>GdIJpD_33rqy>*EY&=SJ+LWIo3@>NIr}-` z#{9Q48buvnN{zc29Lr3dZz+aGh`+BtDkF*nJb=@yIBunPNI!bk2L*4k+-Ie!T(^Ti zW~b=2)VetK-bs`gC+6d0_(LNg;UX^aHaq|Nketn64(sTLvK`TsXw|O|5)#-e>(t|8 zD_vY&(T^j-=sCp-870r!Ok8k*K6dYFXD=;n%}aMro@$ARyvys4-U)7yVg|X|`)+O* zMV%DuNa8Jj$H#8=ayVNH8-%b3Ia@3xo}OW35_wToq#L#M)zmIoszHyBA|X+mSxVo^ zQcsWS^JfbwrV5)$QU=kj#{SH?$T%llfRqB1kzY_Qyw+JNKNayfSmG3+XYU|0KbUJ~ zZtMp{c^QUfCX0R50&Uh`Ux?AtzEl5hbiNVrr6S45Cc+sYKflxS$|sHtpZF>(`k*i!cT4!h{>;pu z+uYK~bV9e?;gbr=prF{@Hejx*(x{(k4H=5pt>vNW))3y`k2EsLYU;RGcRr>TNCLXLLNM@%{21ywh{{c$`XF6g z`uK7#DsjassAPS^4qt0;&qxwambrykQ_sb20Bz5JT4J5}5g(s?jk(Tn28HD_jk#YQ z9<6*2ppS~8Oo1$t`vhX9>72bgMZA7J;nt$c{@4eHTPjia6gzRbJM)|Ne<8OnTp$_C zE0nJShz9QddtscjGY?{q#ef^nZvRAA!VckNV`B#CnW?>PjZq-ajTf|0n=RI4*YG+Wgu|sG(`^8ASNiE2($e!zsIhDU#79HpnyNEG(HL zT#8Ch0qccF#O;%!kHyS#pK_^;F`P-GsHSsUS2?Yox_PAXmfo~%y4$QY?O4TtPZ7_T z`r~M^C25)Bre0p+sHohZySA*SD<(iB8P)GR)%bdBe(O~#H-0c(~oX+FN>kS)+)Tu9b37XXqUFnHBAmtw$DrGwm(R|L*nNa2own^L{6`85Z-!UmtXJG zWY4obUzfa9h*~Suo*i|xe?1I>A75l<<6bfQjlcdJ7HYsO<+Q%_!`GUet?AP+6*k2f zOYGdI@kT?S{0?ZUa~(o|%AR^fPkUQggWAO3zeD|6CC3MkCld~50uSYZ4nfDn5Sx(h zN>=IkDB}1q1ozIXgdhb|V|9^(qV#frk;KHu$6;Zi9%k0Yu&u}@Bq)>#j@!A`P+c2B zGaY23zTrcZ48nXN7Z@1AfHxr3G4}8hw4(M^SZYkDS#e=u&~or|t9PrYm`LwhC@Mp9 zJEo|7_fDfT5GQ(vy{7kXZ!s9PZMeL!sAAN$POYw?2eoij)joC}V%DOfdl7-R#9Rmw zW;fic`k!mW#4#K1KYJb=e@#xjIvvdsnb z$F^2K@_@E?MaU@JWqtK>Bdfef!k3QA_W9!EH#vFRI5PmS}cOLy3H=F@`xPWxz zPK|Sj<47TP95*%<4@DQk;M8*?^}~8Sc|(c_%gepK0OF55-9*K!(-AwqxY!8Bj!I)4 zor_jFy7nT>XJqD?20wQb85$z5A5>09x^G>3TWLEt*crQ5;^lS3Z-mE~EEa&hAoL+W zr?%cXW@I-M9#&o0^CidjWR47O6|@=y-sGL@>^X7G66r$J|2Z0i{eiS}LTx>L*K$qO zHO(?Poh$3p`W(gUZJP%?OHPWRd{Et%8!4*BBcWd3-8L}eK0{yr4c<#gWM)X@zChyr^ATaPMP5F;_evlp*l59bvV-Xuu7}!y3;qsdu63FpulRQl{y1zw>Jumb%-(3bd#gG1<4IsOQv#EXV}&Ts3}B)1f8nJQgww%JS(ntMvR zFH~L&AFrh)Z*Gd*n!)ma?17Ni-Ow)gF7!Jmclr~%C^It%wb}CAR{Bhv^{96Vy`Wl) zrL8Hh8z*P&2TuuBtEo`-~T|O?&nl*Wyi+0C$O3p{eLl<||P@RHrspM4EYW6PocJj}Y!9fqRrh&XoBBsRm zxkr{?>Evh1>IzQV2m1DBv}~KmKN&4K8ok_c{Wk3jsR}1VqwxMnc+7c_lfm2HH2_r` zyz^9Cfb&|$aH4_yG)O@;sEdgFX?u4SeWUOoarZO%7AJxJIkneOEO(Jc*1rn=w223v zFu8r({Ra~eMHTw@OLHkq@B18{=$|I~C@O=0yTpZC|7o^|PWi-k(tn`_|F+ZH1&jUu zpDIb6$=f(z8W8_t6TAWb5iPMCcIh`lq2EMQR5clB7NQj2A7FqJ7vRFbf0IX^g8BG= z;0w^(j{m>!W`@G~_j}Ss|NjCXFqn#B-5RRCzCI}G;{eVCQ|A8+XQ=S-aLQ$1EgsyP zB9YufobLB}(OIhgJv{v1AnD8Y;0=(D8GZqu8xt4D%+F78`}S>N)Xz(bN=jIW_HYJO zS7k7>k|%qHfS?f%QDXfAzOb+py^){@hze;HS69*6+S+&4Vp01@(zK#{}JAUpK`kOSSl#eCr(W(wy1 z0#nJ~j76AZgXnX-CzUps0$xhh8npbwHaC3^g4Cpyk$>OAFKBP8x0{vzhY$n=oq(nc zfxvKr2nrTy^0Kkvs;H>E5_4WF8&mr|ga;%4(h0!%LkwXsQDv`CjEjqF*b+!e*}x=@ zx&V1S-ny+v88%FR)~br(m+JMuK_dNvqN28v%StHb1gpccsk0WB0IBh3_1-(&z%V3e z|N46=F{gXo{I3u({~tUGrI5R>t^gSpiYn%h( zFM)JP{A0M4bb9tKiU6I7gF+UK>^m| zwd1X|$tQ*zh#8+KCnLGAZU1xTqn)#JKtKR4>w`Ceuof-2lG8C(x3%>g$j%SnD{%Ss zO+7yV)aSJ8khx?ei2o8@OBjkV$A)^_!HiSjfDAt zb=ViLglrk%_OXS8gwCEj2mKsRw!^bKKh$0xr&dP`7#%*+sP(teSNq!FZ>2w2N98uQ zBO8k%`r4LL?L{=ymP1yYHvXzj1!yU^ls}%W!vPs|%o88&|DwM-6-3T<-Q+V3tYmD! zElZo5v%h%pA~HTcI|>Cm12ZqMQZJB_`d|oGmIiZ`WB`M!;C% zf(l;&+7tlcbRkz`6*j)Twry%`L||A{V0QUzr^Ge2wJokp!N!T%u^z}$LU_Z^g-`|C z%Oi6@CffuzLkjf|jD?~Xv}%M$q!)kuL0NMDtk=;7ZvDX$=gM>g69CKP3=C#euSNwg z(M|BJ0L&1k#067m2zQT1LUI>~$uPXOmn9^0SvES4fz?IWZHEiwTbC&B z4rzKowIVM9%)o$VEifF0GD^^ag&nPSW4(P1ZkEU$=8W}vJ7F1{&d}ONezY-7pR2VlA#P;YJC4J4$ko5 zME-v_(I2QR19xPHP=BWQ@;D8wh3E7#TM?Htw1LjHix`CEbNHB~Jh>d4oZ>){E4>ft zib8cbt&FCX^!9%G6t#bN*cHws#X}F1X9XOjbRpYN-|N)#+&2#M@KnEi`Eseyw0l_} zTD<~1@7G2jY$ysE6PJyH8CwTH z>c$0x4lcg!`PvO2r)LDy7gwlT^ByW%P10Ju$r8qZAhi973SOJ|z=FBStE{Y&ju+L@ zv0`pJ1)slu)dJS1I=P_uq$F<4uaAMQk1ixWF_B|=yt0^!on0O@irL1?&7C&U4Xfy7oP&^7bDjLr?q4na9p@2A>_zxX@h8Noyr7C5g@0ViZlXInT!{-EUW0*u@ZQ`7Gj3yyGS zrKBu!9TSt2#&xdSFO`zTwVXhAJbP@|3p+g3GHqqW3L4B~W7`a4;DO!riWM zTD?F)aTb0ZN++N&S7{LIwzWu#G&JOUHh(Xyb+Io4mzYU16u^eJSfka;C4#q0(%^zv z-jAMSKvy+3Hull+?lciu#?l9!fz|tldSW?r#h1t9t6@VNK2n9*;#D4#QmWunpEIux z%U#|RkfR^C-dVnveUfbA)CqX$%ofrqiNO;7=aBZdrB!;?-`^hvT4QwL$?33!h`xUP zN+aoBB`0|bJ5mmM=KTSlFCifT0U8qYsZc*M7B zrj}ryw6<2f065difJW|(h=U!Z0YQhJ2-V;Bqk;vt#RfpEiJ`lu0Ue|2}4el{5JdT+ZkvKY6n)SWsj7%)p_A- z)ipJSUs7*l-hKufn_fIwkyR$1NB8Fx2HUEmt82eHCSWyPuRDkDdgsnrcXxM?O1&B8 z?rCIn1>;hO%Br+Wc zf8H)xolQ_2HY=gOZ*R{B$3h}tV?TGE%8WlQJDY81Z89V@ln~?aF;fM`BmXLg@-l>6 zc<0YI!;A{t$atFveXD8cQ?QK~DP4@AVYwU5zb9+5?@NmdkrzY_XAl_n>Hz?Z!Zh}y zUNAz8kR}sgQO+<8&X5d>Z?GpbpdcpfxI{t4qk|_VR)dq`VQgUFZQF26#pHFwm+W=o z0wBbXKms8tDM_@sUy;-$&{xktT;ZFyL09dO%bm%Vkey1wYqm zYFN&9`W$-FDa(92Y3b;Ozb2@TtR#<-ZLQZI5)Tg#(+F6^D3nFexn=d9YAAlG==dk# zhrWFXYv{s^&CE3Un7_i22UzMDVUcK-o2mG=eL!t6W7aa5XtmnM+S;!_Q1sK5qP|w2V)F>L%1Oc7{ z0!---L!6I52j%asPq#z7ArO~ZX+D4hYeoi&^v|9>GcYv`&?+-YgOC=2hmPT4D(_bo z*?T`zFf_$&ZE|xPeq9n3pt|8ai{!M1J!#9v;UQ)=FTL=&clX zqCq>109->0xc-b18i<_mh=^MA)bnSSm(L@B51NHXxB4$4dO@@Kog8?HX5;Ry zw{Mj-frXvCC~u z>o95no;Or_ET;x?C{JAh#3KcXU||(+du%67{@R`P!aNu3pJIjak)mD@MJ`sdZz5T$7p>kTERp7h;i`qo(vMe z;otFn7hDROarGGlfzmLP0%XM#Fm2FlJ`fzDrF8AuwH(1m0EQX4#?qOUm319vT#ci( z#IPMicbUw@O+Yiv0Bj}ru;!s5O7Ck@Y|6=2@3IS|EElMIq47peQ}dOa&0$VR*4S$} z&Zys?hnV`h)#&xi2hvKPIy!DCD0HeyeBGA^_G-v|K9_v=L-yn zSJ>Dzjy%E8>j1Lh@#Dv4(8TB4{-nz8GRBP!KD=IlT9OD~BL`3N5 zHDR(S?N&F0Galw`+}hcwrK>(!fw@8jbG+<8_ri;LhJ;e`6mVu%B_XESVT zfKbVpnO%czhtXHGFq+0J4YwUDa5Rm7GIz3yiiAx~O_=j0_0vBI1DiTz4*5EqxbEJ& zhlk1Sq|2Vi9L?c~(^~8Di=PbW+`arNDg-dC&|r9Gp#IYcLFe-E!|Lju1yNB^qOtm% zXRBYpW9n`|pi5Wok;|zEe|+EbI<{K&zm@;}qN}^x`U~vK&HhO1(7ciNaE3e@&%Kev zfB+XjA|zB9Jl!Q$KA14+@V$p&qQ+VIhb2XqZ8Qeitfoc*M*)CGm6#qJd1bR4vQ6pi z$DAB27t=8zZn*E+x$iH;6EUvKE7KDZ6AyRf^m?5h>bJDF&p|j&sVwGkxLOHB&+9l5 z>e2RdZD}z3dwX*c0-O7A$Zi7`-Kd0wAPAruCnp(hn)VEi%KSuZL?!|Y9JJOy0SE#m zIMQJ=KA5-)mV!UDjAP6(@Re44WO|{_bK?dX zsLJ29u;92th7ZvR9|S7FDn;4qe{u()eTh*vxtIB<>cfYpCrA6+YY+1{r@&xrfQeCm zQq)<_?>L9CS=cy)IU63}OrY(_>I({pKwA#i8zSHVfZYt6Ea)tdj<&*~v#;=pP=(@;tOgFq`i}xCNo0 zMJ;X}F!h09eC)ak3emSA*?~zWmRpVxL)bfH@(5lP@j3lCmG~z^b@~WwdNVxd1)$L6 ztN9KgtF)S$TC2kq$)le#{FcLnU^_g+QZShXBV*&IAoWaH%&qw@70K#{_I{aVf`6*| zPmZ>)VGt6=VGy>1^7A{SQ!m73v5cFqFtn_?x)qYn;P?meX;Njuv=?;O5AINEJEZG~9Pezxfx z=C}Y|b23&|D`gW#m=Dcv!hC4?OGrz>djitanUA(d^z9rRnn89JAF&4!>Fnxim`a9R z+WmMQ4BHkc*-OHSa-G&z03K20_|N5yfCm*6w9qiPZ4jVAZ;}fx9BnY#Bp`Krj+eLC zv@QsfOC<`%B&*H2ZEbCjVC7*#JWTp-e*Q1WkEFqg!)&blN7?UF*L1F!%Gl&mX?+EHid^7TDX*#*ni#s_itwXKl1$lvcdjmtKk2< k^?yd-e@5W{q7m4W68JePqU}&ygMdHsvJdVQNgKTQUm?S@7ytkO literal 0 HcmV?d00001 diff --git a/doc/sphinx/source/recipes/figures/weathertyping/lwt_1_ERA5__psl_mean_1958-2014.png b/doc/sphinx/source/recipes/figures/weathertyping/lwt_1_ERA5__psl_mean_1958-2014.png new file mode 100755 index 0000000000000000000000000000000000000000..b2cba03df67ceaf0e59e121c71894431a1f1783c GIT binary patch literal 111271 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 KdZn68*#7{hpN|dz literal 0 HcmV?d00001 diff --git a/doc/sphinx/source/recipes/figures/weathertyping/slwt_EOBS_4_ERA5__psl_mean_1958-2014.png b/doc/sphinx/source/recipes/figures/weathertyping/slwt_EOBS_4_ERA5__psl_mean_1958-2014.png new file mode 100755 index 0000000000000000000000000000000000000000..1f15102db781db3c7172ec836e56498786620dfe GIT binary patch literal 102843 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 np.array: + """Calculate simplified weathertypes for observation datasets based on \ + precipitation patterns over specified area. + + Args: + cfg (dict): Configuration dictionary from recipe + lwt (np.array): Array of Lamb WT + cube (iris.cube.Cube): preprocessor cube to keep time coordinate + dataset (str): Name of dataset + correlation_thresold (float): correlation_threshold + rmse_threshold (float): rsme_threshold + ancestors (list): list of ancestors + + Returns + np.array: _description_ + """ + logger.info("Calculating simplified Lamb Weathertypes for %s", dataset) + + work_dir = cfg.get("work_dir") + correlation_threshold = cfg.get("correlation_threshold") + rmse_threshold = cfg.get("rmse_threshold") + tcoord = cube.coord("time") + + wt_data_prcp = [] + for wt_ in range(1, 28): + target_indices = np.where(lwt == wt_) + if len(target_indices[0]) < 1: + logger.info( + "calc_slwt_obs - CAUTION: Skipped wt %s \ + for dataset %s!", + wt_, + dataset, + ) + continue + dates = [ + tcoord.units.num2date(tcoord.points[i]) for i in target_indices + ] + if dataset == "E-OBS": + extracted_cube = cube[target_indices] + else: + extracted_cube = cube.extract( + iris.Constraint(time=lambda t, d=dates: t.point in d[0]) + ) + wt_cube_mean = extracted_cube.collapsed("time", iris.analysis.MEAN) + wt_data_prcp.append(wt_cube_mean.data.compressed()) + selected_pairs = process_prcp_mean( + cfg, + wt_data_prcp, + correlation_threshold, + rmse_threshold, + dataset, + timerange, + ) + + with open( + f"{work_dir}/wt_selected_pairs_{dataset}.json", "w", encoding="utf-8" + ) as file: + json.dump(selected_pairs, file) + + mapping_dict = get_mapping_dict(selected_pairs) + + write_mapping_dict(work_dir, dataset, mapping_dict) + + provenance_record = get_provenance_record( + "Lamb Weathertypes", ancestors, ["Lamb Weathertypes"], False, False + ) + + log_provenance( + f"{work_dir}/wt_selected_pairs_{dataset}", cfg, provenance_record + ) + + return map_lwt_to_slwt(lwt, mapping_dict) + + +def calc_const(): + """Calculate constants for weathertyping algorithm. + + Eq. taken from: Jones, P.D., Hulme, M. and Briffa, K.R. (1993), + A comparison of Lamb circulation types with an objective classification + scheme. + Int. J. Climatol., 13: 655-663. https://doi.org/10.1002/joc.3370130606 + + Returns + tuple: The four constants needed for WT calculation. + """ + const1 = 1 / np.cos(45 * np.pi / 180) + const2 = np.sin(45 * np.pi / 180) / np.sin(40 * np.pi / 180) + const3 = np.sin(45 * np.pi / 180) / np.sin(50 * np.pi / 180) + const4 = 1 / (2 * np.cos(45 * np.pi / 180) ** 2) + + return const1, const2, const3, const4 + + +def calc_westerly_flow(cube: iris.cube.Cube) -> np.array: + """Calculate the westerly flow over area. + + Eq. taken from: Jones, P.D., Hulme, M. and Briffa, K.R. (1993), + A comparison of Lamb circulation types with an objective classification + scheme. + Int. J. Climatol., 13: 655-663. https://doi.org/10.1002/joc.3370130606 + + Args: + cube (iris.cube.Cube): Cube of psl data. + + Returns + np.array: westerly flow + """ + return 1 / 2 * (cube.data[:, 1, 2] + cube.data[:, 1, 4]) - 1 / 2 * ( + cube.data[:, 3, 2] + cube.data[:, 3, 4] + ) + + +def calc_southerly_flow(cube: iris.cube.Cube, const1: float) -> np.array: + """Calculate the southerly flow over area. + + Eq. taken from: Jones, P.D., Hulme, M. and Briffa, K.R. (1993), + A comparison of Lamb circulation types with an objective classification + scheme. + Int. J. Climatol., 13: 655-663. https://doi.org/10.1002/joc.3370130606 + + Args: + cube (iris.cube.Cube): Cube of psl data. + const1 (float): const1 + + Returns + np.array: southerly flow + """ + return const1 * ( + 1 + / 4 + * (cube.data[:, 3, 4] + 2 * cube.data[:, 2, 4] + cube.data[:, 1, 4]) + - 1 + / 4 + * (cube.data[:, 3, 2] + 2 * cube.data[:, 2, 2] + cube.data[:, 1, 2]) + ) + + +def calc_resultant_flow( + westerly_flow: np.array, southerly_flow: np.array +) -> np.array: + """Calculate the resultant flow. + + Eq. taken from: Jones, P.D., Hulme, M. and Briffa, K.R. (1993), + A comparison of Lamb circulation types with an objective classification + scheme. + Int. J. Climatol., 13: 655-663. https://doi.org/10.1002/joc.3370130606 + + Args: + westerly_flow (np.array): westerly flow. + southerly_flow (np.array): southerly flow + + Returns + np.array: resultant flow + """ + return (southerly_flow**2 + westerly_flow**2) ** (1 / 2) + + +def calc_westerly_shear_velocity( + cube: iris.cube.Cube, const2: float, const3: float +) -> np.array: + """Calculate westerly shear velocity. + + Eq. taken from: Jones, P.D., Hulme, M. and Briffa, K.R. (1993), + A comparison of Lamb circulation types with an objective classification + scheme. + Int. J. Climatol., 13: 655-663. https://doi.org/10.1002/joc.3370130606 + + Args: + cube (iris.cube.Cube): cube of psl data + const2 (float): const2 + const3 (float): const3 + + Returns + np.array: westerly shear velocity + """ + return const2 * ( + 1 / 2 * (cube.data[:, 0, 2] + cube.data[:, 0, 4]) + - 1 / 2 * (cube.data[:, 2, 2] + cube.data[:, 2, 4]) + ) - const3 * ( + 1 / 2 * (cube.data[:, 2, 2] + cube.data[:, 2, 4]) + - 1 / 2 * (cube.data[:, 4, 2] + cube.data[:, 4, 4]) + ) + + +def calc_southerly_shear_velocity( + cube: iris.cube.Cube, const4: float +) -> np.array: + """Calculate southerly shear velocity. + + Eq. taken from: Jones, P.D., Hulme, M. and Briffa, K.R. (1993), + A comparison of Lamb circulation types with an objective classification + scheme. + Int. J. Climatol., 13: 655-663. https://doi.org/10.1002/joc.3370130606 + + Args: + cube (iris.cube.Cube): cube of psl data + const4 (float): const4 + + Returns + np.array: southerly shear velocity + """ + return const4 * ( + 1 + / 4 + * (cube.data[:, 3, 6] + 2 * cube.data[:, 2, 6] + cube.data[:, 1, 6]) + - 1 + / 4 + * (cube.data[:, 3, 4] + 2 * cube.data[:, 2, 4] + cube.data[:, 1, 4]) + - 1 + / 4 + * (cube.data[:, 3, 2] + 2 * cube.data[:, 2, 2] + cube.data[:, 1, 2]) + + 1 + / 4 + * (cube.data[:, 3, 0] + 2 * cube.data[:, 2, 0] + cube.data[:, 1, 0]) + ) + + +def calc_total_shear_velocity( + westerly_shear_velocity: np.array, southerly_shear_velocity: np.array +) -> np.array: + """Calculate total shear velocity. + + Eq. taken from: Jones, P.D., Hulme, M. and Briffa, K.R. (1993), + A comparison of Lamb circulation types with an objective classification + scheme. + Int. J. Climatol., 13: 655-663. https://doi.org/10.1002/joc.3370130606 + + Args: + westerly_shear_velocity (np.array): westerly shear velocity + southerly_shear_velocity (np.array): southerly shear velocity + + Returns + np.array: total shear velocity + """ + return westerly_shear_velocity + southerly_shear_velocity + + +def wt_algorithm(cube: iris.cube.Cube, dataset: str) -> np.array: + """Algorithm to calculate Lamb weathertypes. + + Eq. taken from: Jones, P.D., Hulme, M. and Briffa, K.R. (1993), + A comparison of Lamb circulation types with an objective classification + scheme. + Int. J. Climatol., 13: 655-663. https://doi.org/10.1002/joc.3370130606 + + Args: + cube (iris.cube.Cube): PSL field of dataset + dataset (str): Name of dataset + + Returns + np.array: Array of Lamb WT for each day + """ + # lats and lons corresponding to datapoints + # 55, 5 -> 1 + # 55, 15 -> 2 + # 50, -5 -> 3 + # 50, 5 -> 4 + # 50, 15 -> 5 + # 50, 25 -> 6 + # 45, -5 -> 7 + # 45, 5 -> 8 + # 45, 15 -> 9 + # 45, 25 -> 10 + # 40, -5 -> 11 + # 40, 5 -> 12 + # 40, 15 -> 13 + # 40, 25 -> 14 + # 35, 5 -> 15 + # 35, 15 -> 16 + + # lons: -5, 0, 5, 10, 15, 20, 25 + # lats: 35, 40, 45, 50, 55 + + logger.info("Calculating Lamb Weathertypes for %s", dataset) + + const1, const2, const3, const4 = calc_const() + + # westerly flow + westerly_flow = calc_westerly_flow(cube) + # southerly flow + southerly_flow = calc_southerly_flow(cube, const1) + # resultant flow + total_flow = calc_resultant_flow(westerly_flow, southerly_flow) + # westerly shear vorticity + westerly_shear_velocity = calc_westerly_shear_velocity( + cube, const2, const3 + ) + # southerly shear vorticity + southerly_shear_velocity = calc_southerly_shear_velocity(cube, const4) + # total shear vorticity + total_shear_velocity = calc_total_shear_velocity( + westerly_shear_velocity, southerly_shear_velocity + ) + + weathertypes = np.zeros(len(total_shear_velocity)) + + for i, z_i in enumerate(total_shear_velocity): + direction = ( + np.arctan(westerly_flow[i] / southerly_flow[i]) * 180 / np.pi + ) # deg + if southerly_flow[i] >= 0: + direction += 180 # deg + + if direction < 0: + direction += 360 # deg + + # Lamb pure directional type + if abs(z_i) < total_flow[i]: + if 337.5 <= direction or direction < 22.5: + weathertypes[i] = 1 + elif 22.5 <= direction < 67.5: + weathertypes[i] = 2 + elif 67.5 <= direction < 112.5: + weathertypes[i] = 3 + elif 112.5 <= direction < 157.5: + weathertypes[i] = 4 + elif 157.5 <= direction < 202.5: + weathertypes[i] = 5 + elif 202.5 <= direction < 247.5: + weathertypes[i] = 6 + elif 247.5 <= direction < 292.5: + weathertypes[i] = 7 + elif 292.5 <= direction < 337.5: + weathertypes[i] = 8 + # Lamb’s pure cyclonic and anticyclonic type + elif (2 * total_flow[i]) < abs(z_i): + if z_i > 0: + weathertypes[i] = 9 + + elif z_i < 0: + weathertypes[i] = 10 + # Lambs’s synoptic/direction hybrid types + elif total_flow[i] < abs(z_i) < (2 * total_flow[i]): + if z_i > 0: + if 337.5 <= direction or direction < 22.5: + weathertypes[i] = 11 + elif 22.5 <= direction < 67.5: + weathertypes[i] = 12 + elif 67.5 <= direction < 112.5: + weathertypes[i] = 13 + elif 112.5 <= direction < 157.5: + weathertypes[i] = 14 + elif 157.5 <= direction < 202.5: + weathertypes[i] = 15 + elif 202.5 <= direction < 247.5: + weathertypes[i] = 16 + elif 247.5 <= direction < 292.5: + weathertypes[i] = 17 + elif 292.5 <= direction < 337.5: + weathertypes[i] = 18 + + elif z_i < 0: + if 337.5 <= direction or direction < 22.5: + weathertypes[i] = 19 + elif 22.5 <= direction < 67.5: + weathertypes[i] = 20 + elif 67.5 <= direction < 112.5: + weathertypes[i] = 21 + elif 112.5 <= direction < 157.5: + weathertypes[i] = 22 + elif 157.5 <= direction < 202.5: + weathertypes[i] = 23 + elif 202.5 <= direction < 247.5: + weathertypes[i] = 24 + elif 247.5 <= direction < 292.5: + weathertypes[i] = 25 + elif 292.5 <= direction < 337.5: + weathertypes[i] = 26 + # light indeterminate flow, corresponding to Lamb’s unclassified type U + elif abs(z_i) < 6 and total_flow[i] < 6: + weathertypes[i] = 27 + + return weathertypes + + +def calc_lwt_slwt_model( + cfg: dict, + cube: iris.cube.Cube, + data_info: dict, + predefined_slwt: bool | dict, +): + """Calculate Lamb WT and simplified WT for model data. + + Args: + cfg (dict): Configuration dicitonary from recipe + cube (iris.cube.Cube): PSL field of dataset + data_info (dict): Dictionary with info to dataset + predefined_slwt (bool | dict): If False, automatic_slwt will be used. + If dict, this mapping dict will be used. + (see recipe option predefined_slwt) + """ + work_dir = cfg.get("work_dir") + dataset = data_info.get("dataset") + preproc_path = data_info.get("preproc_path") + output_file_path = data_info.get("output_file_path") + ensemble = data_info.get("ensemble", "") + timerange = data_info.get("timerange") + driver = data_info.get("driver", "") + if driver != "": + driver = f"_{driver}" + + if not os.path.exists(f"{work_dir}/{output_file_path}"): + os.makedirs(f"{work_dir}/{output_file_path}") + + lwt = wt_algorithm(cube, dataset) + + tcoord = cube.coord("time") + time_points = tcoord.units.num2date(tcoord.points) + + logger.info( + "Calculating simplified Lamb Weathertypes for %s %s", dataset, ensemble + ) + + if not predefined_slwt: + with open( + f"{work_dir}/wt_mapping_dict_ERA5.json", encoding="utf-8" + ) as file: + mapping_dict_era5_f = json.load(file) + + with open( + f"{work_dir}/wt_mapping_dict_E-OBS.json", encoding="utf-8" + ) as file: + mapping_dict_eobs_f = json.load(file) + mapping_dict_era5 = reverse_convert_dict(mapping_dict_era5_f) + mapping_dict_eobs = reverse_convert_dict(mapping_dict_eobs_f) + + slwt_era5 = map_lwt_to_slwt(lwt, mapping_dict_era5) + slwt_eobs = map_lwt_to_slwt(lwt, mapping_dict_eobs) + else: + predefined_slwt = check_mapping_dict_format(predefined_slwt) + write_mapping_dict(work_dir, "ERA5", predefined_slwt) + write_mapping_dict(work_dir, "E-OBS", predefined_slwt) + slwt_era5 = map_lwt_to_slwt(lwt, predefined_slwt) + slwt_eobs = map_lwt_to_slwt(lwt, predefined_slwt) + + wt_cube = iris.cube.CubeList() + wt_cube.append(iris.cube.Cube(lwt, long_name="lwt")) + wt_cube.append(iris.cube.Cube(slwt_era5, long_name="slwt_era5")) + wt_cube.append(iris.cube.Cube(slwt_eobs, long_name="slwt_eobs")) + + wt_cube[0].add_dim_coord(tcoord, 0) + wt_cube[1].add_dim_coord(tcoord, 0) + wt_cube[2].add_dim_coord(tcoord, 0) + + iris.save( + wt_cube, + f"{work_dir}/{output_file_path}/{dataset}{driver}_" + f"{ensemble}_{timerange}.nc", + ) + + # write to csv file + d = { + "date": time_points[:], + "lwt": np.int8(lwt), + "slwt_ERA5": np.int8(slwt_era5), + "slwt_EOBS": np.int8(slwt_eobs), + } + df = pd.DataFrame(data=d) + df.to_csv( + f"{work_dir}/{output_file_path}/{dataset}{driver}_{ensemble}_" + f"{timerange}.csv", + index=False, + ) + + ancestors = [ + preproc_path, + f"{work_dir}/wt_mapping_dict_ERA5.json", + f"{work_dir}/wt_mapping_dict_E-OBS.json", + ] + provenance_record = get_provenance_record( + "Lamb Weathertypes", ancestors, ["Lamb Weathertypes"], False, False + ) + + log_provenance( + f"{work_dir}/{output_file_path}/{dataset}_{ensemble}", + cfg, + provenance_record, + ) + + +def rmse(subarray1: np.array, subarray2: np.array) -> np.array: + """Calculate rsme. + + Args: + subarray1 (np.array): array1 + subarray2 (np.array): array2 + + Returns + np.array: rsme array + """ + return np.sqrt(np.mean((subarray1 - subarray2) ** 2)) + + +def process_prcp_mean( + cfg: dict, + data: np.array, + correlation_threshold: float, + rmse_threshold: float, + dataset: str, + timerange: str, +) -> list: + """Process precipitation fields. + + Specified area to get a list of selected pairs of weathertypes + with the highest correlation (higher than correlation_threshold) + and smallest RSME (smaller than rsme_threshold) for + further processing and simplifying the WT. + + Args: + cfg (dict): Configuration dictionary from recipe + data (np.array): Precipitation data + correlation_threshold (float): Correlation threshold + rmse_threshold (float): RMSE threshold + dataset (str): Name of dataset + + Returns + list: Selected pairs of WT. This is passed to get_mapping_dict + """ + logger.info("Calculating corr and rsme matrices for %s", dataset) + + selected_pairs = [] + pattern_correlation_matrix = np.ma.corrcoef(data) + n = len(data) + rmse_matrix = np.zeros((n, n)) + + for i in range(n): + for j in range(i + 1, n): + rmse_matrix[i][j] = rmse(data[i], data[j]) + rmse_matrix[j][i] = rmse_matrix[i][j] + if ( + pattern_correlation_matrix[i][j] >= correlation_threshold + and rmse_matrix[i][j] <= rmse_threshold + ): + selected_pairs.append( + ( + (i + 1, j + 1), + pattern_correlation_matrix[i][j], + rmse_matrix[i][j], + ) + ) + + # write matrices to csv + write_corr_rmse_to_csv( + cfg, pattern_correlation_matrix, rmse_matrix, dataset + ) + # plot heatmaps for matrices + plot_corr_rmse_heatmaps( + cfg, pattern_correlation_matrix, rmse_matrix, dataset, timerange + ) + + return selected_pairs + + +def calc_wt_means( + cfg: dict, + cube: iris.cube.Cube, + wt_cubes: iris.cube.CubeList, + data_info: dict, +): + """Calculate means of fields of each weathertype. + + Args: + cfg (dict): Configuration dictionary from recipe + cube (iris.cube.Cube): Cube with variable data + wt_cubes (iris.cube.CubeList): List of cubes of lwt, slwt_ERA5 + and slwt_EOBS + data_info (dict): Dictionary with info to dataset + preproc_path (str): Ancestor path + """ + dataset = data_info.get("dataset") + var_name = data_info.get("var") + wt_string = data_info.get("wt_string") + preproc_path = data_info.get("preproc_path") + ensemble = data_info.get("ensemble") + timerange = data_info.get("timerange") + driver = data_info.get("driver", "") + if driver != "": + driver = f"_{driver}" + + logger.info("Calculating %s %s means for %s", dataset, var_name, wt_string) + + work_dir = cfg.get("work_dir") + + num_slwt = 0 + target_indices = [] + lwt, slwt_eobs, slwt_era5 = [], [], [] + + if wt_string == "slwt_ERA5": + slwt_era5_cube = wt_cubes[1] + tcoord = slwt_era5_cube.coord("time") + slwt_era5 = slwt_era5_cube.data[:] + num_slwt = len(np.unique(slwt_era5)) + elif wt_string == "slwt_EOBS": + slwt_eobs_cube = wt_cubes[2] + tcoord = slwt_eobs_cube.coord("time") + slwt_eobs = slwt_eobs_cube.data[:] + num_slwt = len(np.unique(slwt_eobs)) + elif wt_string == "lwt": + lwt_cube = wt_cubes[0] + tcoord = lwt_cube.coord("time") + lwt = lwt_cube.data[:] + + if "slwt" in wt_string: + for wt in range(1, num_slwt + 1): + if wt_string == "slwt_ERA5": + target_indices = np.where(slwt_era5 == wt) + elif wt_string == "slwt_EOBS": + target_indices = np.where(slwt_eobs == wt) + else: + logger.info("WT_STRING not supported!") + if len(target_indices[0]) < 1: + logger.info( + "calc_wt_means - CAUTION: Skipped %s %s \ + for dataset %s!", + wt_string, + wt, + dataset, + ) + continue + dates = [ + tcoord.units.num2date(tcoord.points[i]) for i in target_indices + ] + extracted_cube = cube.extract( + iris.Constraint(time=lambda t, d=dates: t.point in d[0]) + ) + wt_cube_mean = extracted_cube.collapsed("time", iris.analysis.MEAN) + plot_maps(wt, cfg, wt_cube_mean, data_info, "mean") + elif wt_string == "lwt": + for wt in range(1, 28): + target_indices = np.where(lwt == wt) + if len(target_indices[0]) < 1: + logger.info( + "calc_wt_means - CAUTION: Skipped lwt %s \ + for dataset %s!", + wt, + dataset, + ) + continue + dates = [ + tcoord.units.num2date(tcoord.points[i]) for i in target_indices + ] + extracted_cube = cube.extract( + iris.Constraint(time=lambda t, d=dates: t.point in d[0]) + ) + wt_cube_mean = extracted_cube.collapsed("time", iris.analysis.MEAN) + plot_maps(wt, cfg, wt_cube_mean, data_info, "mean") + else: + logger.info("WT_STRING NOT SUPPORTED.") + + ancestors = [f"{preproc_path}", f"{work_dir}/ERA5.nc"] + provenance_record = get_provenance_record( + f"{var_name} means for \ + {wt_string}", + ancestors, + [var_name], + ["map"], + ["mean"], + ) + + local_path = f"{cfg.get('plot_dir')}/mean" + + log_provenance( + f"{local_path}/{wt_string}_{wt}{driver}_" + f"{dataset}_{ensemble}" + f"_{var_name}_mean_{timerange}", + cfg, + provenance_record, + ) + + +def calc_wt_anomalies( + cfg: dict, + cube: iris.cube.Cube, + wt_cubes: iris.cube.CubeList, + data_info: dict, +): + """Calculate anomalies of fields of each weathertype. + + Args: + cfg (dict): Configuration dictionary from recipe + cube (iris.cube.Cube): Cube with variable data + wt_cubes (iris.cube.CubeList): List of cubes of lwt, slwt_ERA5 + and slwt_EOBS + data_info (dict): Dictionary with info to dataset + preproc_path (str): Ancestor path + """ + work_dir = cfg.get("work_dir") + dataset = data_info.get("dataset") + var_name = data_info.get("var_name") + wt_string = data_info.get("wt_string") + preproc_path = data_info.get("preproc_path") + ensemble = data_info.get("ensemble") + timerange = data_info.get("timerange") + + logger.info( + "Calculating %s %s anomalies for %s", dataset, var_name, wt_string + ) + + target_indices = [] + lwt, slwt_eobs, slwt_era5 = [], [], [] + + if wt_string == "slwt_ERA5": + slwt_era5_cube = wt_cubes[1] + tcoord = slwt_era5_cube.coord("time") + slwt_era5 = slwt_era5_cube.data[:] + elif wt_string == "slwt_EOBS": + slwt_eobs_cube = wt_cubes[2] + tcoord = slwt_eobs_cube.coord("time") + slwt_eobs = slwt_eobs_cube.data[:] + elif wt_string == "lwt": + lwt_cube = wt_cubes[0] + tcoord = lwt_cube.coord("time") + lwt = lwt_cube.data[:] + + num_slwt_era5 = len(np.unique(slwt_era5)) + num_slwt_eobs = len(np.unique(slwt_eobs)) + + if num_slwt_eobs != num_slwt_era5: + logger.info( + "calc_wt_anomalies - CAUTION: unequal number of \ + slwt_era5 (%s) and slwt_eobs (%s)!", + num_slwt_era5, + num_slwt_eobs, + ) + + if "slwt" in wt_string: + for wt in range(1, max(num_slwt_era5, num_slwt_eobs)): + if wt_string == "slwt_ERA5": + target_indices = np.where(slwt_era5 == wt) + elif wt_string == "slwt_EOBS": + target_indices = np.where(slwt_eobs == wt) + else: + logger.info("WT_STRING not supported!") + if len(target_indices[0]) < 1: + logger.info( + "calc_wt_anomalies - CAUTION: Skipped wt %s \ + for dataset %s!", + wt, + dataset, + ) + continue + dates = [ + tcoord.units.num2date(tcoord.points[i]) for i in target_indices + ] + extracted_cube = cube.extract( + iris.Constraint(time=lambda t, d=dates: t.point in d[0]) + ) + wt_cube_mean = extracted_cube.collapsed("time", iris.analysis.MEAN) + plot_maps( + wt, + cfg, + cube.collapsed("time", iris.analysis.MEAN) - wt_cube_mean, + data_info, + "anomaly", + ) + elif wt_string == "lwt": + for wt in range(1, 28): + target_indices = np.where(lwt == wt) + if len(target_indices[0]) < 1: + logger.info( + "calc_wt_anomalies - CAUTION: Skipped wt %s \ + for dataset %s!", + wt, + dataset, + ) + continue + dates = [ + tcoord.units.num2date(tcoord.points[i]) for i in target_indices + ] + extracted_cube = cube.extract( + iris.Constraint(time=lambda t, d=dates: t.point in d[0]) + ) + wt_cube_mean = extracted_cube.collapsed("time", iris.analysis.MEAN) + plot_maps( + wt, + cfg, + cube.collapsed("time", iris.analysis.MEAN) - wt_cube_mean, + data_info, + "anomaly", + ) + else: + logger.info("WT_STRING NOT SUPPORTED.") + + ancestors = [f"{preproc_path}", f"{work_dir}/ERA5.nc"] + provenance_record = get_provenance_record( + f"{var_name} anomaly for \ + {wt_string}", + ancestors, + [var_name], + ["map"], + ["anomaly"], + ) + + local_path = f"{cfg.get('plot_dir')}/anomaly" + + log_provenance( + f"{local_path}/{wt_string}_{wt}_{dataset}_{ensemble}" + f"_{var_name}_anomaly__{timerange}", + cfg, + provenance_record, + ) + + +def calc_wt_std( + cfg: dict, + cube: iris.cube.Cube, + wt_cubes: iris.cube.CubeList, + data_info: dict, +): + """Calculate standard deviation of fields of each weathertype. + + Args: + cfg (dict): Configuration dictionary from recipe + cube (iris.cube.Cube): Cube with variable data + wt_cubes (iris.cube.CubeList): List of cubes of lwt, slwt_ERA5 + and slwt_EOBS + data_info (dict): Dictionary with info to dataset + preproc_path (str): Ancestor path + """ + work_dir = cfg.get("work_dir") + dataset = data_info.get("dataset") + var_name = data_info.get("var_name") + wt_string = data_info.get("wt_string") + preproc_path = data_info.get("preproc_path") + ensemble = data_info.get("ensemble") + timerange = data_info.get("timerange") + + logger.info( + "Calculating %s %s standard deviation for %s", + dataset, + var_name, + wt_string, + ) + + target_indices = [] + lwt, slwt_eobs, slwt_era5 = [], [], [] + + if wt_string == "slwt_ERA5": + slwt_era5_cube = wt_cubes[1] + tcoord = slwt_era5_cube.coord("time") + slwt_era5 = slwt_era5_cube.data[:] + elif wt_string == "slwt_EOBS": + slwt_eobs_cube = wt_cubes[2] + tcoord = slwt_eobs_cube.coord("time") + slwt_eobs = slwt_eobs_cube.data[:] + elif wt_string == "lwt": + lwt_cube = wt_cubes[0] + tcoord = lwt_cube.coord("time") + lwt = lwt_cube.data[:] + + num_slwt_era5 = len(np.unique(slwt_era5)) + num_slwt_eobs = len(np.unique(slwt_eobs)) + + if num_slwt_eobs != num_slwt_era5: + logger.info( + "calc_wt_std - CAUTION: unequal number of \ + slwt_era5 (%s) and slwt_eobs (%s)!", + num_slwt_era5, + num_slwt_eobs, + ) + + if "slwt" in wt_string: + for wt in range(1, max(num_slwt_era5, num_slwt_eobs)): + if wt_string == "slwt_ERA5": + target_indices = np.where(slwt_era5 == wt) + elif wt_string == "slwt_EOBS": + target_indices = np.where(slwt_eobs == wt) + else: + logger.info("WT_STRING not supported!") + if len(target_indices[0]) < 1: + logger.info( + "calc_slwt_obs - CAUTION: Skipped wt %s \ + for dataset %s!", + wt, + dataset, + ) + continue + dates = [ + tcoord.units.num2date(tcoord.points[i]) for i in target_indices + ] + extracted_cube = cube.extract( + iris.Constraint(time=lambda t, d=dates: t.point in d[0]) + ) + wt_cube_std = extracted_cube.collapsed( + "time", iris.analysis.STD_DEV + ) + plot_maps(wt, cfg, wt_cube_std, data_info, "stddev") + elif wt_string == "lwt": + for wt in range(1, 28): + target_indices = np.where(lwt == wt) + if len(target_indices[0]) < 1: + logger.info( + "calc_wt_std - CAUTION: Skipped wt %s \ + for dataset %s!", + wt, + dataset, + ) + continue + dates = [ + tcoord.units.num2date(tcoord.points[i]) for i in target_indices + ] + extracted_cube = cube.extract( + iris.Constraint(time=lambda t, d=dates: t.point in d[0]) + ) + wt_cube_std = extracted_cube.collapsed( + "time", iris.analysis.STD_DEV + ) + plot_maps(wt, cfg, wt_cube_std, data_info, "stddev") + else: + logger.info("WT_STRING NOT SUPPORTED.") + + ancestors = [f"{preproc_path}", f"{work_dir}/ERA5.nc"] + provenance_record = get_provenance_record( + f"{var_name} standard \ + deviation for \ + {wt_string}", + ancestors, + [var_name], + ["map"], + ["stddev"], + ) + + local_path = f"{cfg.get('plot_dir')}/stddev" + + log_provenance( + f"{local_path}/{wt_string}_{wt}_{dataset}_{ensemble}" + f"_{var_name}_stddev_{timerange}", + cfg, + provenance_record, + ) + + +def calc_lwt_model( + cfg: dict, cube: iris.cube.Cube, dataset: str, data_info: dict +): + """Calculate lwt for model data. + + Args: + cfg (dict): Configuration dictionary from recipe + cube (iris.cube.Cube): Cube to keep time coordinate + dataset (str): Name of dataset + data_info (dict): Dictionary with info to dataset + """ + work_dir = cfg.get("work_dir") + dataset = data_info.get("dataset") + output_file_path = data_info.get("output_file_path") + ensemble = data_info.get("ensemble", "") + timerange = data_info.get("timerange") + driver = data_info.get("driver", "") + if driver != "": + driver = f"_{driver}" + + if not os.path.exists(f"{work_dir}/{output_file_path}"): + os.makedirs(f"{work_dir}/{output_file_path}") + + wt = wt_algorithm(cube, dataset) + + tcoord = cube.coord("time") + time_points = tcoord.units.num2date(tcoord.points) + + wt_cube = iris.cube.CubeList() + wt_cube.append(iris.cube.Cube(wt, long_name="lwt")) + + logger.info( + "Writing Lamb Weathertype for %s \ + to file %s.nc", + dataset, + dataset, + ) + + wt_cube[0].add_dim_coord(tcoord, 0) + + iris.save( + wt_cube, + f"{work_dir}/{output_file_path}/{dataset}{driver}" + f"_{ensemble}_{timerange}.nc", + ) + + # write to csv file + d = {"date": time_points[:], "lwt": np.int8(wt)} + df = pd.DataFrame(data=d) + df.to_csv( + f"{work_dir}/{output_file_path}/{dataset}{driver}_{ensemble}_" + f"{timerange}.csv", + index=False, + ) + + ancestors = [ + f"{work_dir}/wt_mapping_dict_ERA5.json", + f"{work_dir}/wt_mapping_dict_E-OBS.json", + ] + provenance_record = get_provenance_record( + "Lamb Weathertypes", ancestors, ["Lamb Weathertypes"], False, False + ) + + log_provenance( + f"{work_dir}/{output_file_path}/{dataset}_{ensemble}", + cfg, + provenance_record, + ) + + +def plot_means( + cfg: dict, + preproc_var: np.array, + wt_cubes: iris.cube.Cube, + data_info: dict, + only_lwt=False, +): + """Wrapper function to plot various means/std/anomalies. + + Args: + cfg (dict): cfg dictionary provided by recipe + preproc_var (np.array): variable to be plotted + wt_cubes (iris.cube.Cube): list of wt cubes + data_info (dict): dictionary with info to dataset + only_lwt (bool, optional): If True, + only Lamb weathertypes will be loaded. Defaults to False. + (useful for automatic_slwt = False) + """ + if not only_lwt: + data_info["wt_string"] = "lwt" + calc_wt_means(cfg, preproc_var, wt_cubes, data_info) + data_info["wt_string"] = "slwt_ERA5" + calc_wt_means(cfg, preproc_var, wt_cubes, data_info) + data_info["wt_string"] = "slwt_EOBS" + calc_wt_means(cfg, preproc_var, wt_cubes, data_info) + else: + data_info["wt_string"] = "lwt" + calc_wt_means(cfg, preproc_var, wt_cubes, data_info) diff --git a/esmvaltool/diag_scripts/weathertyping/plot_utils.py b/esmvaltool/diag_scripts/weathertyping/plot_utils.py new file mode 100644 index 0000000000..bbad9c8c24 --- /dev/null +++ b/esmvaltool/diag_scripts/weathertyping/plot_utils.py @@ -0,0 +1,408 @@ +"""Utility functions for plotting.""" + +# operating system manipulations (e.g. path constructions) +import logging +import os +import warnings + +# plotting imports +import cartopy.crs as ccrs +import cartopy.feature as cfeature + +# to manipulate iris cubes +import iris +import iris.analysis.cartography +import iris.plot as iplt +import matplotlib.pyplot as plt +import matplotlib.ticker as mticker + +# general imports +import numpy as np +import seaborn as sns + +# local imports +from cartopy.mpl.gridliner import LATITUDE_FORMATTER, LONGITUDE_FORMATTER +from matplotlib.colors import ListedColormap + +iris.FUTURE.datum_support = True + +logger = logging.getLogger(os.path.basename(__file__)) + +# Ignoring a warning that is produced when selecting timesteps of a weathertype +warnings.filterwarnings("ignore", ".*Collapsing a non-contiguous coordinate*") + + +def generate_grayscale_hex_values(x): + """Generate grayscale values for plotting seasonal occurrence. + + Args: + x (int): number of weathertypes + + Returns + np.list: array with grescale values as hex + """ + grayscale_values = np.linspace(0, 1, x) + grayscale_hex = [ + f"#{int(value * 255):02x}{int(value * 255):02x}{int(value * 255):02x}" + for value in grayscale_values + ] + + return grayscale_hex + + +def plot_seasonal_occurrence( + cfg: dict, wt_cubes: iris.cube.Cube, data_info: dict +): + """Plot relative monthly/seasonal occurrence of weathertypes. + + Args: + cfg (dict): Configuration dict from recipe + wt_cubes (iris.cube.Cube): Cube with weathertypes + data_info (dict): Dictionary with relevant info to dataset + """ + dataset_name = data_info.get("dataset") + timerange = data_info.get("timerange") + ensemble = data_info.get("ensemble", "") + driver = data_info.get("driver", "") + + output_path = f"{cfg['plot_dir']}/seasonal_occurrence" + + if not os.path.exists(f"{output_path}"): + os.makedirs(f"{output_path}") + + month_list = [ + "Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec", + ] + + relative_occurrences = {} + # {wt_string: {month: {wt1: rel_occurrence, wt2: rel_occurrence, ....}}} + # first do absolute occurrence, then relative occurrence + for cube in wt_cubes: + month_dict = {} + for month in range(1, 13): + month_constraint = iris.Constraint( + time=iris.time.PartialDateTime(month=month) + ) + array = cube.extract(month_constraint).data + unique, counts = np.unique(array, return_counts=True) + count_dict = dict(zip(unique, counts / sum(counts), strict=False)) + month_dict[month] = count_dict + relative_occurrences[cube.long_name] = month_dict + + x = month_list + + for wt_string in relative_occurrences: + wt_numbers = max( + len(value) + for value in relative_occurrences.get(wt_string).values() + ) + colors = generate_grayscale_hex_values(wt_numbers) + wt_stack = np.zeros((wt_numbers, 12)) + for month, month_value in relative_occurrences.get(wt_string).items(): + print(month_value) + for wt in month_value.keys(): + print(month_value.get(wt)) + wt_stack[np.int8(wt - 1), month - 1] = month_value.get(wt) + + y = np.vstack(list(wt_stack)) + + # plot + _, ax_ = plt.subplots(figsize=(10, 10)) + + ax_.set_title( + f"Seasonal occurence of {wt_string}, \ + {dataset_name}, {timerange}" + ) + + ax_.stackplot(x, y, colors=colors) + + ax_.legend( + loc="upper center", + bbox_to_anchor=(0.5, -0.05), + fancybox=True, + shadow=True, + ncol=9, + labels=tuple(f"WT {i + 1}" for i in range(0, wt_numbers)), + ) + + ax_.set( + xlim=(0, 11), + xticks=np.arange(0, 12), + ylim=(0, 1), + yticks=np.arange(0, 1.1, 0.1), + ) + ax_.set_xlabel("Month") + ax_.set_ylabel("Cumulative Relative occurrence") + + plt.savefig( + f"{output_path}/{driver}{dataset_name}_{ensemble}_" + f"{wt_string}_rel_occurrence_{timerange}.png" + ) + plt.savefig( + f"{output_path}/{driver}{dataset_name}_{ensemble}_" + f"{wt_string}_rel_occurrence_{timerange}.pdf" + ) + plt.close() + + +def plot_maps( + wt: np.array, cfg: dict, cube: iris.cube.Cube, data_info: dict, mode: str +): + """Plot maps. + + Args: + wt (np.array): WT for which statistic was calculated + cfg (dict): Configuration dicitonary from recipe + cube (iris.cube.Cube): Data to be plotted + data_info (dict): Dictionary with info to dataset + mode (str): Statistics that is used + """ + dataset = data_info.get("dataset") + var_name = data_info.get("var") + wt_string = data_info.get("wt_string") + ensemble = data_info.get("ensemble", "") + timerange = data_info.get("timerange") + driver = data_info.get("driver", "") + if driver != "": + driver = f"_{driver}" + + logger.info( + "Plotting %s %s %s for %s %s", dataset, var_name, mode, wt_string, wt + ) + + local_path = f"{cfg.get('plot_dir')}/{mode}" + + if not os.path.exists(f"{local_path}"): + os.makedirs(f"{local_path}") + + ax = plt.axes(projection=ccrs.PlateCarree()) + + if var_name == "psl": + psl_cmap = get_colormap("psl") + plt.title( + f"{dataset} {ensemble}, {var_name} {mode}\n" + + f"{timerange}, wt: {wt}" + ) + unit = "[hPa]" + im = iplt.contourf(cube / 100, cmap=psl_cmap) + cb = plt.colorbar(im) + cb.ax.tick_params(labelsize=8) + cb.set_label(label=f"{var_name} {mode} {unit}") + elif var_name == "pr": + prcp_cmap = get_colormap("prcp") + if dataset == "ERA5": + unit = "[m]" + plt.title( + f"{dataset} {ensemble}, total {var_name} {mode}\n" + + f"{timerange}, wt: {wt}" + ) + else: + unit = "[kg m-2 s-1]" + plt.title( + f"{dataset} {ensemble}, {var_name} flux {mode}\n" + + f"{timerange}, wt: {wt}" + ) + im = iplt.contourf(cube, cmap=prcp_cmap) + cb = plt.colorbar(im) + cb.ax.tick_params(labelsize=8) + cb.set_label(label=f"{var_name} {mode} {unit}") + elif var_name == "tas": + temp_cmap = get_colormap("temp") + unit = "[K]" + plt.title( + f"{dataset} {ensemble}, 1000 hPa {var_name} {mode}\n" + + f"{timerange}, wt: {wt}" + ) + im = iplt.contourf(cube, cmap=temp_cmap) + cb = plt.colorbar(im) + cb.ax.tick_params(labelsize=8) + cb.set_label(label=f"{var_name} {mode} {unit}") + + gl = ax.gridlines( + crs=ccrs.PlateCarree(), + draw_labels=True, + linewidth=0.5, + color="gray", + alpha=0.5, + linestyle="--", + ) + gl.left_labels = True + gl.bottom_labels = True + gl.top_labels = False + gl.right_labels = False + gl.xlines = True + gl.ylocator = mticker.FixedLocator(np.arange(20, 70, 5)) + gl.xlocator = mticker.FixedLocator([-10, -5, 0, 5, 10, 15]) + gl.xformatter = LONGITUDE_FORMATTER + gl.yformatter = LATITUDE_FORMATTER + gl.xlabel_style = {"size": 8, "color": "black"} + gl.ylabel_style = {"color": "black", "size": 8} + + ax.set_extent([-15, 20, 27.5, 62.5]) + + ax.coastlines() + ax.add_feature(cfeature.BORDERS, linestyle=":") + + plt.savefig( + f"{local_path}/{wt_string}_{wt}{driver}_{dataset}_{ensemble}" + f"_{var_name}_{mode}_{timerange}.png" + ) + plt.savefig( + f"{local_path}/{wt_string}_{wt}{driver}_{dataset}_{ensemble}_" + f"{var_name}_{mode}_{timerange}.pdf" + ) + plt.close() + + +def plot_corr_rmse_heatmaps( + cfg: dict, + pattern_correlation_matrix: np.array, + rmse_matrix: np.array, + dataset: str, + timerange: str, +): + """Plot heatmaps for correlation and rmse matrices. + + Args: + cfg (dict): cfg dict from recipe + pattern_correlation_matrix (np.array): pattern correlation matrix + rmse_matrix (np.array): rmse matrix + dataset (str): string of dataset + timerange (str): string of timerange + """ + output_path = f"{cfg.get('plot_dir')}/heatmaps" + + if not os.path.exists(f"{output_path}"): + os.makedirs(f"{output_path}") + + labels = np.arange(1, 28) + + mask = np.zeros_like(pattern_correlation_matrix) + mask[np.triu_indices_from(mask)] = True + with sns.axes_style("white"): + plt.figure(figsize=(10, 10)) + plt.title(f"Correlation Matrix , {dataset}, {timerange}") + levels = np.linspace( + np.min(pattern_correlation_matrix), + np.max(pattern_correlation_matrix), + 9, + ) + ax = sns.heatmap( + pattern_correlation_matrix, + mask=mask, + square=True, + annot=True, + annot_kws={"size": 6}, + cmap="seismic", + xticklabels=labels, + yticklabels=labels, + cbar_kws={"ticks": levels, "shrink": 0.8, "format": "%.2f"}, + ) + ax.set_xlabel("lwt", fontsize=8) + ax.set_xticklabels(ax.get_xticklabels(), rotation=0) + ax.set_yticklabels(ax.get_yticklabels(), rotation=0) + ax.set_ylabel("lwt", fontsize=8) + plt.tight_layout() + plt.savefig( + f"{output_path}/correlation_matrix_{dataset}_{timerange}.png" + ) + plt.savefig( + f"{output_path}/correlation_matrix_{dataset}_{timerange}.pdf" + ) + plt.close() + + mask = np.zeros_like(rmse_matrix) + mask[np.triu_indices_from(mask)] = True + with sns.axes_style("white"): + plt.figure(figsize=(10, 10)) + plt.title(f"RMSE Matrix, {dataset}, {timerange}") + levels = np.linspace(np.min(rmse_matrix), np.max(rmse_matrix), 9) + ax = sns.heatmap( + rmse_matrix, + mask=mask, + square=True, + annot=True, + annot_kws={"size": 5}, + cmap="seismic", + xticklabels=labels, + yticklabels=labels, + cbar_kws={"ticks": levels, "shrink": 0.8, "format": "%.2f"}, + ) + ax.set_xlabel("lwt", fontsize=8) + ax.set_xticklabels(ax.get_xticklabels(), rotation=0) + ax.set_yticklabels(ax.get_yticklabels(), rotation=0) + ax.set_ylabel("lwt", fontsize=8) + plt.tight_layout() + plt.savefig(f"{output_path}/rmse_matrix_{dataset}_{timerange}.png") + plt.savefig(f"{output_path}/rmse_matrix_{dataset}_{timerange}.pdf") + plt.close() + + +def get_colormap(colormap_string: str) -> ListedColormap: + """Get colormaps based on string. + + Args: + colormap_string (str): String to get Colormaps for either + psl, tas or precipitation. + + Returns + ListedColormap: Choosen Colormap + """ + misc_seq_2_disc = [ + (230 / 255, 240 / 255, 240 / 255), + (182 / 255, 217 / 255, 228 / 255), + (142 / 255, 192 / 255, 226 / 255), + (118 / 255, 163 / 255, 228 / 255), + (116 / 255, 130 / 255, 222 / 255), + (121 / 255, 97 / 255, 199 / 255), + (118 / 255, 66 / 255, 164 / 255), + (107 / 255, 40 / 255, 121 / 255), + (86 / 255, 22 / 255, 75 / 255), + (54 / 255, 14 / 255, 36 / 255), + ] + + temp_seq_disc = [ + (254 / 255, 254 / 255, 203 / 255), + (251 / 255, 235 / 255, 153 / 255), + (244 / 255, 204 / 255, 104 / 255), + (235 / 255, 167 / 255, 84 / 255), + (228 / 255, 134 / 255, 80 / 255), + (209 / 255, 98 / 255, 76 / 255), + (164 / 255, 70 / 255, 66 / 255), + (114 / 255, 55 / 255, 46 / 255), + (66 / 255, 40 / 255, 24 / 255), + (25 / 255, 25 / 255, 0 / 255), + ] + + prec_seq_disc = [ + (255 / 255, 255 / 255, 229 / 255), + (217 / 255, 235 / 255, 213 / 255), + (180 / 255, 216 / 255, 197 / 255), + (142 / 255, 197 / 255, 181 / 255), + (105 / 255, 177 / 255, 165 / 255), + (67 / 255, 158 / 255, 149 / 255), + (44 / 255, 135 / 255, 127 / 255), + (29 / 255, 110 / 255, 100 / 255), + (14 / 255, 85 / 255, 74 / 255), + (0 / 255, 60 / 255, 48 / 255), + ] + + if colormap_string == "psl": + return ListedColormap(misc_seq_2_disc) + if colormap_string == "prcp": + return ListedColormap(prec_seq_disc) + if colormap_string == "temp": + return ListedColormap(temp_seq_disc) + + return None diff --git a/esmvaltool/diag_scripts/weathertyping/weathertyping.py b/esmvaltool/diag_scripts/weathertyping/weathertyping.py new file mode 100644 index 0000000000..6ea5138932 --- /dev/null +++ b/esmvaltool/diag_scripts/weathertyping/weathertyping.py @@ -0,0 +1,315 @@ +"""Script for calculating Lamb weathertypes. + +It also plots the means and seasonal occurrence of the +weathertypes, and offers the option to calculate simplified +weathertypes based on precipitation patterns. +""" + +import iris + +# import internal esmvaltool modules here +from esmvaltool.diag_scripts.shared import run_diagnostic +from esmvaltool.diag_scripts.weathertyping.calc_utils import ( + calc_lwt_model, + calc_lwt_slwt_model, + calc_slwt_obs, + wt_algorithm, +) +from esmvaltool.diag_scripts.weathertyping.plot_utils import ( + plot_means, + plot_seasonal_occurrence, +) +from esmvaltool.diag_scripts.weathertyping.wt_utils import ( + add_dict_entry, + combine_wt_to_file, + get_ancestors_era5_eobs, + get_cfg_vars, + get_looping_dict, + get_model_output_filepath, + get_preproc_lists_ensemble, + get_provenance_record, + load_wt_files, + load_wt_preprocessors, + log_provenance, + run_predefined_slwt, + write_lwt_to_file, +) + + +def run_automatic_slwt(cfg: dict): + """Run the automated calculation for simplified weathertypes \ + and write to file, and plot the means and seasonal occurrence \ + of the weathertypes. + + Args: + cfg (dict): Nested dictionary of metadata + """ + preproc_variables_dict, _, _, work_dir, plotting, _, predefined_slwt = ( + get_cfg_vars(cfg) + ) + for dataset_name, dataset_vars in preproc_variables_dict.items(): + data_info = { + "timerange": dataset_vars[0].get("timerange").replace("/", "-"), + "dataset": dataset_name, + } + if dataset_name == "ERA5": + wt_preproc, wt_preproc_prcp, wt_preproc_prcp_eobs = ( + load_wt_preprocessors(dataset_name, preproc_variables_dict) + ) + + # calculate lwt + lwt = wt_algorithm(wt_preproc, dataset_name) + + era5_ancestors, eobs_ancestors = get_ancestors_era5_eobs( + dataset_name, preproc_variables_dict + ) + + # calculate simplified lwt based on precipitation + # patterns or use predefined_slwt + if not predefined_slwt: + slwt_era5 = calc_slwt_obs( + cfg, + lwt, + wt_preproc_prcp, + dataset_name, + era5_ancestors, + data_info["timerange"], + ) + slwt_eobs = calc_slwt_obs( + cfg, + lwt, + wt_preproc_prcp_eobs, + "E-OBS", + eobs_ancestors, + data_info["timerange"], + ) + else: + slwt_era5, slwt_eobs = run_predefined_slwt( + work_dir, dataset_name, lwt, predefined_slwt + ) + + # write to file + wt_list = [lwt, slwt_era5, slwt_eobs] + combine_wt_to_file(cfg, wt_list, wt_preproc, dataset_name) + + # load weathertype files as cubes + wt_cubes = load_wt_files(f"{work_dir}/{dataset_name}.nc") + + var_dict = get_looping_dict( + dataset_vars + ) # dataset_vars is list of variables for dataset dataset_name + if plotting: + # plot means + for var_name, var_data in var_dict.items(): + add_dict_entry(data_info, "var", var_name) + add_dict_entry(data_info, "preproc_path", var_data[1]) + + plot_means(cfg, var_data[0], wt_cubes, data_info) + plot_seasonal_occurrence(cfg, wt_cubes, data_info) + else: + if dataset_name == "E-OBS": + continue + for ensemble_var in dataset_vars: + if ensemble_var.get("preprocessor") == "weathertype_preproc": + add_dict_entry( + data_info, "ensemble", ensemble_var.get("ensemble", "") + ) + add_dict_entry( + data_info, "driver", ensemble_var.get("driver", "") + ) + if data_info["driver"] != "": + data_info["driver"] = "_" + {data_info["driver"]} + + wt_preproc = iris.load_cube(ensemble_var.get("filename")) + + output_file_path, preproc_path = get_model_output_filepath( + dataset_name, ensemble_var + ) + + data_info["output_file_path"] = output_file_path + data_info["preproc_path"] = preproc_path + + # calculate weathertypes + calc_lwt_slwt_model( + cfg, wt_preproc, data_info, predefined_slwt + ) + + # load wt files + wt_cubes = load_wt_files( + f"{work_dir}/{output_file_path}" + f"/{dataset_name}" + f"{data_info['driver']}_" + f"{data_info['ensemble']}_" + f"{data_info['timerange']}.nc" + ) + + var_dict = { + f"{ensemble_var.get('short_name')}": get_preproc_lists_ensemble( + ensemble_var + ) + } + + # plot means + if plotting: + for var_name, var_data in var_dict.items(): + add_dict_entry(data_info, "var", var_name) + add_dict_entry( + data_info, "preproc_path", var_data[1] + ) + + plot_means(cfg, var_data[0], wt_cubes, data_info) + plot_seasonal_occurrence(cfg, wt_cubes, data_info) + + +def run_lwt(cfg: dict): + """Run calculation of weathertypes and write to file, and plot the means \ + and seasonal occurrence of the weathertypes. + + Args: + cfg (dict): Nested dictionary of metadata + + """ + preproc_variables_dict, _, _, work_dir, plotting, _, _ = get_cfg_vars(cfg) + for dataset_name, dataset_vars in preproc_variables_dict.items(): + data_info = { + "timerange": dataset_vars[0].get("timerange").replace("/", "-"), + "dataset": dataset_name, + } + + if dataset_name == "ERA5": + wt_preproc, _, _ = load_wt_preprocessors( + dataset_name, preproc_variables_dict + ) + + # calculate lwt + lwt = wt_algorithm(wt_preproc, dataset_name) + + era5_ancestors, eobs_ancestors = get_ancestors_era5_eobs( + dataset_name, preproc_variables_dict + ) + + ancestors = [era5_ancestors, eobs_ancestors] + + provenance_record = get_provenance_record( + "Lamb Weathertypes", + ancestors, + ["Lamb Weathertypes"], + False, + False, + ) + + log_provenance(f"{work_dir}/lwt_era5", cfg, provenance_record) + + # write only lwt to file + write_lwt_to_file(cfg, lwt, wt_preproc, dataset_name) + + # load wt files + wt_cubes = load_wt_files( + f"{work_dir}/{dataset_name}.nc", only_lwt=True + ) + + var_dict = get_looping_dict( + dataset_vars + ) # dataset_vars is list of variables for dataset dataset_name + + if plotting: + # plot means + for var_name, var_data in var_dict.items(): + add_dict_entry(data_info, "var", var_name) + add_dict_entry(data_info, "preproc_path", var_data[1]) + + plot_means( + cfg, var_data[0], wt_cubes, data_info, only_lwt=True + ) + plot_seasonal_occurrence(cfg, wt_cubes, data_info) + else: + if dataset_name == "E-OBS": + continue + for ensemble_var in dataset_vars: + if ensemble_var.get("preprocessor") == "weathertype_preproc": + add_dict_entry( + data_info, "ensemble", ensemble_var.get("ensemble", "") + ) + add_dict_entry( + data_info, "driver", ensemble_var.get("driver", "") + ) + if data_info["driver"] != "": + data_info["driver"] = "_" + {data_info["driver"]} + + wt_preproc = iris.load_cube( + dataset_vars[0].get("filename") + ) + + output_file_path, preproc_path = get_model_output_filepath( + dataset_name, dataset_vars + ) + + # calculate weathertypes + data_info["output_file_path"] = output_file_path + data_info["preproc_path"] = preproc_path + + calc_lwt_model(cfg, wt_preproc, dataset_name, data_info) + + # load wt files + wt_cubes = load_wt_files( + f"{work_dir}/{output_file_path}" + f"/{dataset_name}" + f"{data_info['driver']}" + f"_{data_info['ensemble']}_" + f"{data_info['timerange']}.nc", + only_lwt=True, + ) + + var_dict = get_looping_dict( + dataset_vars + ) # dataset_vars is list of variables for dataset_name + + if plotting: + # plot means + for var_name, var_data in var_dict.items(): + add_dict_entry(data_info, "var", var_name) + add_dict_entry( + data_info, "preproc_path", var_data[1] + ) + + plot_means( + cfg, + var_data[0], + wt_cubes, + data_info, + only_lwt=True, + ) + plot_seasonal_occurrence(cfg, wt_cubes, data_info) + + +def run_my_diagnostic(cfg: dict): + """Run the weathertyping diagnostic. + + Arguments: + cfg - nested dictionary of metadata + + Returns + string; runs the user diagnostic + + """ + # assemble the data dictionary keyed by dataset name + # this makes use of the handy group_metadata function that + # orders the data by 'dataset'; the resulting dictionary is + # keyed on datasets e.g. dict = {'MPI-ESM-LR': [var1, var2...]} + # where var1, var2 are dicts holding all needed information per variable + automatic_slwt = cfg.get("automatic_slwt") + + if automatic_slwt: + run_automatic_slwt(cfg) + # if automatic_slwt is false, and predefined_slwt is false, + # just write selected pairs to file + elif not automatic_slwt: + run_lwt(cfg) + + +if __name__ == "__main__": + # always use run_diagnostic() to get the config (the preprocessor + # nested dictionary holding all the needed information) + with run_diagnostic() as config: + # list here the functions that need to run + run_my_diagnostic(config) diff --git a/esmvaltool/diag_scripts/weathertyping/wt_utils.py b/esmvaltool/diag_scripts/weathertyping/wt_utils.py new file mode 100644 index 0000000000..53dddfe4e2 --- /dev/null +++ b/esmvaltool/diag_scripts/weathertyping/wt_utils.py @@ -0,0 +1,538 @@ +"""Utility functions for weathertyping script.""" + +# operating system manipulations (e.g. path constructions) +import json +import logging +import os +import warnings + +# to manipulate iris cubes +import iris +import iris.analysis.cartography + +# general imports +import numpy as np +import pandas as pd + +# import internal esmvaltool modules here +from esmvaltool.diag_scripts.shared import ProvenanceLogger, group_metadata + +iris.FUTURE.datum_support = True + +logger = logging.getLogger(os.path.basename(__file__)) + +# Ignoring a warning that is produced when selecting timesteps of a weathertype +warnings.filterwarnings("ignore", ".*Collapsing a non-contiguous coordinate*") + + +def add_dict_entry(dict_: dict, key: str, value): + """Add entry to dictionary. + + Args: + dict (dict): dictionary to add entry to + key (str): key of entry + value: value of entry + + Returns + dict: updated dictionary + """ + dict_[key] = value + return dict_ + + +def get_cfg_vars(cfg: dict): + """Get list of vars from configuration dict. + + Args: + cfg (dict): Configuration dict from recipe. + + Returns + tuple: cfg vars + """ + preproc_variables_dict = group_metadata( + cfg.get("input_data").values(), "dataset" + ) + + return ( + preproc_variables_dict, + cfg.get("correlation_threshold"), + cfg.get("rmse_threshold"), + cfg.get("work_dir"), + cfg.get("plotting", False), + cfg.get("automatic_slwt", True), + cfg.get("predefined_slwt", False), + ) + + +def load_wt_preprocessors(dataset: str, preproc_variables_dict: dict): + """Load preprocessor cubes for calculating Lamb weathertypes. + + Args: + dataset (str): dataset name + preproc_variables_dict (dict): dictionary of preprocessor variables + + Returns + _type_: list of preprocessor vars for weathertyping + """ + wt_preproc = iris.load_cube( + preproc_variables_dict.get(dataset)[0].get("filename") + ) + wt_preproc_prcp = iris.load_cube( + preproc_variables_dict.get(dataset)[1].get("filename") + ) + wt_preproc_prcp_eobs = iris.load_cube( + preproc_variables_dict.get("E-OBS")[0].get("filename") + ) + + return wt_preproc, wt_preproc_prcp, wt_preproc_prcp_eobs + + +def get_ancestors_era5_eobs(dataset: str, preproc_variables_dict: dict): + """Get ancestors for ERA5/E-OBS. + + Args: + dataset (str): dataset name + preproc_variables_dict (dict): dictionary of preprocessor variables + + Returns + _type_: lists of ERA5/E-OBS ancestors + """ + era5_ancestors = [ + preproc_variables_dict.get(dataset)[0].get("filename"), + preproc_variables_dict.get(dataset)[1].get("filename"), + ] + eobs_ancestors = [ + preproc_variables_dict.get(dataset)[0].get("filename"), + preproc_variables_dict.get("E-OBS")[0].get("filename"), + ] + return era5_ancestors, eobs_ancestors + + +def get_model_output_filepath(dataset: str, value: list): + """Generate output filepath for model data. + + Args: + dataset (str): Model name + value (list): Model variables + + Returns + _type_: Output filepath and preprocessor path for + future referencing. + """ + timerange = value.get("timerange").replace("/", "-") + experiment = value.get("exp") + ensemble = value.get("ensemble") + driver = value.get("driver", "") + + out_path = f"{dataset}/{driver}/{experiment}/{ensemble}/{timerange}" + preproc_path = value.get("filename") + + return out_path, preproc_path + + +def get_preproc_lists(preproc_vars: list): + """Put preprocessor variables and paths into list for further use. + + Args: + preproc_vars (list): List of variables for specific + dataset. + + Returns + _type_: List of preprocessor cubes for mean calculations + as well as path to files. + """ + preproc_path_psl = preproc_vars[-3].get("filename") + preproc_path_prcp = preproc_vars[-2].get("filename") + preproc_path_tas = preproc_vars[-1].get("filename") + + mean_preproc_psl = iris.load_cube(preproc_vars[-3].get("filename")) + mean_preproc_prcp = iris.load_cube(preproc_vars[-2].get("filename")) + mean_preproc_tas = iris.load_cube(preproc_vars[-1].get("filename")) + + preproc_list = [mean_preproc_psl, mean_preproc_prcp, mean_preproc_tas] + preproc_path_list = [preproc_path_psl, preproc_path_prcp, preproc_path_tas] + + return preproc_list, preproc_path_list + + +def get_preproc_lists_ensemble(preproc_vars: list): + """Put preprocessor variables and paths into list for further use. + + Args: + preproc_vars (list): Variable for specific ensemble member. + + Returns + _type_: Preprocessor cube for mean calculations + as well as path to files. + """ + preproc_path = preproc_vars.get("filename") + + preproc = iris.load_cube(preproc_vars.get("filename")) + + # preproc_list = [mean_preproc] + # preproc_path_list = [preproc_path_psl] + + return preproc, preproc_path + + +def get_looping_dict(preproc_vars: list): + """Put cubes into dictionary for further use. + + Args: + preproc_vars (list): Values of dataset dictionary + + Returns + _type_: Dictionary of the form {'var': [preprov_var, preproc_path]} + """ + preproc, preproc_path = get_preproc_lists(preproc_vars) + dict_ = { + "psl": [preproc[0], preproc_path[0]], + "pr": [preproc[1], preproc_path[1]], + "tas": [preproc[2], preproc_path[2]], + } + return dict_ + + +def load_wt_files(path: str, only_lwt=False): + """Load *.nc files of weathertype data. + + If only_lwt is true, only Lamb + weathertypes will be loaded. (useful for automatic_slwt = False) + + Args: + path (str): Path to weathertype data. + only_lwt (bool, optional): If True, + only Lamb weathertypes will be loaded. Defaults to False. + (useful for automatic_slwt = False) + + Returns + _type_: List of weathertype cubes. + """ + if not only_lwt: + lwt_cube = iris.load_cube(path, "lwt") + slwt_era5_cube = iris.load_cube(path, "slwt_era5") + slwt_eobs_cube = iris.load_cube(path, "slwt_eobs") + wt_cubes = [lwt_cube, slwt_era5_cube, slwt_eobs_cube] + else: + lwt_cube = iris.load_cube(path, "lwt") + wt_cubes = [lwt_cube] + + return wt_cubes + + +def get_provenance_record( + caption: str, + ancestors: list, + long_names: list, + plot_types: bool | list, + statistics: bool | list, +) -> dict: + """Get provenance record. + + Args: + caption (str): Caption for plots + ancestors (list): List of ancestors + long_names (list): Variable long names + plot_types (bool | list): Type of plot. Can be false + if output is not a plot. + statistics (bool | list): Types of statistics used. + + Returns + dict: Provenance dictionary. + """ + record = { + "caption": caption, + "domains": ["reg"], + "authors": ["jury_martin", "kroissenbrunner_thomas"], + "references": ["maraun21jgr", "jones93ijc"], + "projects": ["preval"], + "long_names": long_names, + "ancestors": ancestors, + } + if plot_types is not False: + record["plot_types"] = plot_types + if statistics is not False: + record["statistics"] = statistics + return record + + +def log_provenance(filename: str, cfg: dict, provenance_record: dict): + """Log provenance. Produces xml file provenance info. + + Args: + caption (str): Caption of plots. + filename (str): Output name of provenance. + cfg (dict): Configuration dictionary provided by recipe. + provenance_record (dict): Provenance record dictionary. + """ + with ProvenanceLogger(cfg) as provenance_logger: + provenance_logger.log(filename, provenance_record) + + logger.info("Output stored as %s", filename) + + +def turn_list_to_mapping_dict(list_: list) -> dict: + """Turn list of combined WT into a dictionary for further processing. + + Args: + list_ (list): List where entries are lists with related WT + + Returns + dict: Mapping dicitonary keys are simplified WT, values are Lamb WT + """ + result_dict = {} + + for i, s in enumerate(list_): + for elem in s: + if elem not in result_dict: + result_dict[elem] = i + 1 + else: + result_dict[elem].append(i) + + return result_dict + + +def get_mapping_dict(selected_pairs: list) -> dict: + """Get mapping dictionary from list of selected pairs. + + Args: + selected_pairs (list): Selected pairs of WTs based on + precipitation patterns over specified area and + correlation and RSME thresholds defined in recipe.S + + Returns + dict: Mapping dicitonary keys are simplified WT, values are Lamb WT + """ + mapping_array = [] + + for elem in selected_pairs: + mapping_array.append(elem[0]) + + s = [set(i) for i in mapping_array if i] + + def find_intersection(m_list: list) -> list: + for i, v in enumerate(m_list): + for j, k in enumerate(m_list[i + 1 :], i + 1): + if v & k: + s[i] = v.union(m_list.pop(j)) + return find_intersection(m_list) + return m_list + + merged_tuples = find_intersection(s) + mapping_dict = turn_list_to_mapping_dict(merged_tuples) + + return mapping_dict + + +def write_mapping_dict(work_dir: str, dataset: str, mapping_dict: dict): + """Write mapping dictionary to file. + + Args: + work_dir (str): Working directory + dataset (str): Name of dataset + mapping_dict (dict): Mapping dictionary + """ + mapping_dict_reformat = convert_dict(mapping_dict) + + with open( + f"{work_dir}/wt_mapping_dict_{dataset}.json", "w", encoding="utf-8" + ) as file: + json.dump(mapping_dict_reformat, file) + + +def convert_dict(dict_: dict) -> dict: + """Convert mapping dictionary. + + From {lwt: slwt, ...} format to {slwt: [lwt1, lwt2], ...}. + + Args: + dict_ (dict): Dict in the {lwt: slwt, ...} format + + Returns + dict: Dict in the {slwt: [lwt1, lwt2], ...} format + """ + new_dict = {} + for dataset, value in dict_.items(): + if value not in new_dict: + new_dict[value] = [] + new_dict[value].append(dataset) + return new_dict + + +def reverse_convert_dict(dict_: dict) -> dict: + """Convert mapping dictionary. + + From {slwt: [lwt1, lwt2], ...} format to {lwt: slwt, ...}. + + Args: + original_dict (dict): Dict in the {slwt: [lwt1, lwt2], ...}format + + Returns + dict: Dict in the format {lwt: slwt, ...} + """ + new_dict = {} + for key, value_list in dict_.items(): + for original_key in value_list: + new_dict[original_key] = key + return new_dict + + +def write_corr_rmse_to_csv( + cfg: dict, + pattern_correlation_matrix: np.array, + rmse_matrix: np.array, + dataset: str, +): + """Write correlation and rsme matrix to csv files. + + Args: + cfg (dict): Configuration dictionary from recipe + pattern_correlation_matrix (np.array): Correlation matrix + rmse_matrix (np.array): RSME matrix + dataset (str): Name of dataset + """ + logger.info("Writing corr and rsme matrices for %s", dataset) + + work_dir = cfg.get("work_dir") + + df_corr = pd.DataFrame(pattern_correlation_matrix) + df_corr.index = range(1, len(df_corr) + 1) + df_corr.columns = range(1, len(df_corr.columns) + 1) + df_corr.to_csv( + f"{work_dir}/correlation_matrix_{dataset}.csv", index_label="Index" + ) + + df_rmse = pd.DataFrame(rmse_matrix) + df_rmse.index = range(1, len(df_rmse) + 1) + df_rmse.columns = range(1, len(df_rmse.columns) + 1) + df_rmse.to_csv( + f"{work_dir}/rmse_matrix_{dataset}.csv", index_label="Index" + ) + + +def run_predefined_slwt( + work_dir: str, dataset_name: str, lwt: np.array, predefined_slwt: dict +): + """Run predefined slwt mapping. + + Args: + work_dir (str): Working directory to save mapping dict + dataset_name (str): Name of dataset + lwt (np.array): lwt array + predefined_slwt (dict): Predefined mapping dict + + Returns + np.array: slwt_era5 array + np.array: slwt_eobs array + """ + predefined_slwt = check_mapping_dict_format(predefined_slwt) + write_mapping_dict(work_dir, dataset_name, predefined_slwt) + write_mapping_dict(work_dir, "E-OBS", predefined_slwt) + slwt_era5 = map_lwt_to_slwt(lwt, predefined_slwt) + slwt_eobs = map_lwt_to_slwt(lwt, predefined_slwt) + + return slwt_era5, slwt_eobs + + +def combine_wt_to_file( + cfg: dict, wt_list: list, cube: iris.cube.Cube, file_name: str +): + """Combine lwt and slwt arrays to one file. + + Args: + cfg (dict): Configuration dictionary from recipe + wt_list (list): List of weathertype arrays + cube (iris.cube.Cube): Cube of data to keep time coordinate + file_name (str): Name of output file + """ + lwt = wt_list[0] + slwt_era5 = wt_list[1] + slwt_eobs = wt_list[2] + + logger.info("Writing weathertypes to %s", file_name) + + tcoord = cube.coord("time") + time_points = tcoord.units.num2date(tcoord.points) + + write_path = cfg.get("work_dir") + + wt_cube = iris.cube.CubeList() + wt_cube.append(iris.cube.Cube(lwt, long_name="lwt")) + wt_cube.append(iris.cube.Cube(slwt_era5, long_name="slwt_era5")) + wt_cube.append(iris.cube.Cube(slwt_eobs, long_name="slwt_eobs")) + + wt_cube[0].add_dim_coord(tcoord, 0) + wt_cube[1].add_dim_coord(tcoord, 0) + wt_cube[2].add_dim_coord(tcoord, 0) + + iris.save(wt_cube, f"{write_path}/{file_name}.nc") + + # write to csv file + d = { + "date": time_points[:], + "lwt": np.int8(lwt), + "slwt_era5": np.int8(slwt_era5), + "slwt_eobs": np.int8(slwt_eobs), + } + df = pd.DataFrame(data=d) + df.to_csv(write_path + f"/{file_name}.csv", index=False) + + +def write_lwt_to_file( + cfg: dict, lwt: np.array, cube: iris.cube.Cube, file_name: str +): + """Write only lwt to file. + + Args: + cfg (dict): Configuration dictionary from recipe + lwt (np.array): lwt array + cube (iris.cube.Cube): Cube to keep time coordinate + file_name (str): Output filename + """ + logger.info("Writing Lamb Weathertype to %s", file_name) + + tcoord = cube.coord("time") + time_points = tcoord.units.num2date(tcoord.points) + + write_path = cfg.get("work_dir") + + wt_cube = iris.cube.CubeList() + wt_cube.append(iris.cube.Cube(np.int8(lwt), long_name="lwt")) + + wt_cube[0].add_dim_coord(tcoord, 0) + iris.save(wt_cube, f"{write_path}/{file_name}.nc") + + # write to csv file + d = {"date": time_points[:], "lwt": np.int8(lwt)} + df = pd.DataFrame(data=d) + df.to_csv(write_path + f"/{file_name}.csv", index=False) + + +def map_lwt_to_slwt(lwt: np.array, mapping_dict: dict) -> np.array: + """Map lwt array to slwt array. + + Args: + lwt (np.array): array of lwt + mapping_dict (dict): mapping dictionary in {lwt: slwt, ...} format + + Returns + np.array: array of slwt + """ + return np.array([np.int8(mapping_dict.get(value, 0)) for value in lwt]) + + +def check_mapping_dict_format(mapping_dict: dict) -> dict: + """Check format of mapping dict and return in {lwt: slwt, ...} format. + + Args: + mapping_dict (dict): mapping dict in any format + + Returns + dict: mapping dict in {lwt: slwt, ...} format + """ + if isinstance(mapping_dict.get(list(mapping_dict.keys())[0]), list): + dict_ = reverse_convert_dict(mapping_dict) + else: + dict_ = mapping_dict + + return dict_ diff --git a/esmvaltool/recipes/recipe_weathertyping_CMIP6.yml b/esmvaltool/recipes/recipe_weathertyping_CMIP6.yml new file mode 100644 index 0000000000..737a00e16b --- /dev/null +++ b/esmvaltool/recipes/recipe_weathertyping_CMIP6.yml @@ -0,0 +1,310 @@ +documentation: + title: Weathertyping algorithm + + description: | + A diagnostic to calculate Lamb weathertypes over a given region. Furthermore, + correlations between weathertypes and precipitation patterns over a given area can be calculated + and 'combined' or 'simplified' weathertypes can be derived. Additionally, mean fields, as well as + anomalies and standard deviations can be plotted. + + authors: + - jury_martin + - kroissenbrunner_thomas + + maintainer: + - kroissenbrunner_thomas + + projects: + - preval + + references: + - maraun21jgr + - jones93ijc + +datasets_psl_CMIP6: &datasets_psl_cmip6 + - {institute: AS-RCEC, dataset: TaiESM1, ensemble: r1i1p1f1, grid: gn, esgf_version: v20200626} + - {institute: AWI, dataset: AWI-ESM-1-1-LR, ensemble: r1i1p1f1, grid: gn, esgf_version: v20200212} + - {institute: BCC, dataset: BCC-CSM2-MR, ensemble: r1i1p1f1, grid: gn, esgf_version: v20181126} + - {institute: BCC, dataset: BCC-ESM1, ensemble: r1i1p1f1, grid: gn, esgf_version: v20181220} + - {institute: CAS, dataset: FGOALS-f3-L, ensemble: r1i1p1f1, grid: gr, esgf_version: v20191019} + - {institute: CAS, dataset: FGOALS-g3, ensemble: r1i1p1f1, grid: gn, esgf_version: v20190826} + # - {institute: CCCR-IITM, dataset: IITM-ESM, ensemble: r1i1p1f1, grid: gn, esgf_version: v20210203} #discontinous longitude warning + - {institute: CCCma, dataset: CanESM5, ensemble: r1i1p1f1, grid: gn, esgf_version: v20190429} + - {institute: CMCC, dataset: CMCC-CM2-HR4, ensemble: r1i1p1f1, grid: gn, esgf_version: v20200904} + - {institute: CMCC, dataset: CMCC-CM2-SR5, ensemble: r1i1p1f1, grid: gn, esgf_version: v20200616} + - {institute: CMCC, dataset: CMCC-ESM2, ensemble: r1i1p1f1, grid: gn, esgf_version: v20210114} + - {institute: CNRM-CERFACS, dataset: CNRM-CM6-1, ensemble: r1i1p1f2, grid: gr, esgf_version: v20180917} + - {institute: CSIRO-ARCCSS, dataset: ACCESS-CM2, ensemble: r1i1p1f1, grid: gn, esgf_version: v20191108} + - {institute: CSIRO, dataset: ACCESS-ESM1-5, ensemble: r1i1p1f1, grid: gn, esgf_version: v20191115} + - {institute: EC-Earth-Consortium, dataset: EC-Earth3-AerChem, ensemble: r1i1p1f1, grid: gr, esgf_version: v20200624} + - {institute: EC-Earth-Consortium, dataset: EC-Earth3-CC, ensemble: r1i1p1f1, grid: gr, esgf_version: v20210113} + - {institute: EC-Earth-Consortium, dataset: EC-Earth3-Veg-LR, ensemble: r1i1p1f1, grid: gr, esgf_version: v20200217} + - {institute: EC-Earth-Consortium, dataset: EC-Earth3-Veg, ensemble: r1i1p1f1, grid: gr, esgf_version: v20211207} + - {institute: EC-Earth-Consortium, dataset: EC-Earth3, ensemble: r1i1p1f1, grid: gr, esgf_version: v20200310} + - {institute: HAMMOZ-Consortium, dataset: MPI-ESM-1-2-HAM, ensemble: r1i1p1f1, grid: gn, esgf_version: v20190627} + - {institute: INM, dataset: INM-CM4-8, ensemble: r1i1p1f1, grid: gr1, esgf_version: v20190530} + - {institute: INM, dataset: INM-CM5-0, ensemble: r1i1p1f1, grid: gr1, esgf_version: v20190610} + - {institute: IPSL, dataset: IPSL-CM5A2-INCA, ensemble: r1i1p1f1, grid: gr, esgf_version: v20200729} + # - {institute: IPSL, dataset: IPSL-CM6A-LR-INCA, ensemble: r1i1p1f1, grid: gr, esgf_version: v20210216} #??? + - {institute: IPSL, dataset: IPSL-CM6A-LR, ensemble: r1i1p1f1, grid: gr, esgf_version: v20180803} + - {institute: KIOST, dataset: KIOST-ESM, ensemble: r1i1p1f1, grid: gr1, esgf_version: v20210601} + - {institute: MIROC, dataset: MIROC-ES2L, ensemble: r1i1p1f2, grid: gn, esgf_version: v20191129} + - {institute: MIROC, dataset: MIROC6, ensemble: r1i1p1f1, grid: gn, esgf_version: v20191016} + # - {institute: MPI-M, dataset: ICON-ESM-LR, ensemble: r1i1p1f1, grid: gn, esgf_version: v20210215} #boundary values not 0 or 2 + - {institute: MPI-M, dataset: MPI-ESM1-2-HR, ensemble: r1i1p1f1, grid: gn, esgf_version: v20190710} + - {institute: MPI-M, dataset: MPI-ESM1-2-LR, ensemble: r1i1p1f1, grid: gn, esgf_version: v20190710} + - {institute: MRI, dataset: MRI-ESM2-0, ensemble: r1i1p1f1, grid: gn, esgf_version: v20190603} + - {institute: NASA-GISS, dataset: GISS-E2-2-G, ensemble: r1i1p1f1, grid: gn, esgf_version: v20191120, start_year: 1970} + - {institute: NCAR, dataset: CESM2-FV2, ensemble: r1i1p1f1, grid: gn, esgf_version: v20191120} + - {institute: NCAR, dataset: CESM2-WACCM-FV2, ensemble: r1i1p1f1, grid: gn, esgf_version: v20191120} + # - {institute: NCAR, dataset: CESM2-WACCM, ensemble: r1i1p1f1, grid: gn, esgf_version: v20190227} #concatenate error + - {institute: NCAR, dataset: CESM2, ensemble: r1i1p1f1, grid: gn, esgf_version: v20190308} + - {institute: NCC, dataset: NorCPM1, ensemble: r1i1p1f1, grid: gn, esgf_version: v20200724} + - {institute: NCC, dataset: NorESM2-LM, ensemble: r1i1p1f1, grid: gn, esgf_version: v20190815} + - {institute: NCC, dataset: NorESM2-MM, ensemble: r1i1p1f1, grid: gn, esgf_version: v20191108} + # - {institute: NIMS-KMA, dataset: KACE-1-0-G, ensemble: r1i1p1f1, grid: gr, esgf_version: v20190911} #discontinous longitude coordinate warning + - {institute: NOAA-GFDL, dataset: GFDL-CM4, ensemble: r1i1p1f1, grid: gr1, esgf_version: v20180701} + - {institute: NOAA-GFDL, dataset: GFDL-CM4, ensemble: r1i1p1f1, grid: gr2, esgf_version: v20180701} + - {institute: NOAA-GFDL, dataset: GFDL-ESM4, ensemble: r1i1p1f1, grid: gr1, esgf_version: v20190726} + - {institute: NUIST, dataset: NESM3, ensemble: r1i1p1f1, grid: gn, esgf_version: v20190812} + - {institute: SNU, dataset: SAM0-UNICON, ensemble: r1i1p1f1, grid: gn, esgf_version: v20190323} + +datasets_ERA5: &datasets_era5 + - {project: OBS6, dataset: ERA5, type: reanaly, frequency: day, latestversion: v1, mip: day, tier: 3, start_year: 1958, end_year: 2014} + +datasets_EOBS: &datasets_EOBS + - {dataset: E-OBS, project: OBS, version: v29.0e-0.25, type: ground, tier: 2, start_year: 1958, end_year: 2014, mip: day} + +preprocessors: + weathertype_preproc: + extract_time: &time + start_year: 1950 + start_month: 1 + start_day: 1 + end_year: 2014 + end_month: 12 + end_day: 31 + regrid: + # This is the region for which the Lamb weathertypes will be calculated. + # The objective classification scheme of Jones et al. 1993 is used, + # these grid points are the same ones as used in the paper. + target_grid: &grid + start_longitude: -5 + end_longitude: 25 + step_longitude: 5 + start_latitude: 35 + end_latitude: 55 + step_latitude: 5 + scheme: linear + + weathertype_preproc_pr: + extract_time: + *time + extract_region: + # This is the region for which precipitation data can be used to + # calculate correlations between weathertypes and this said region + # to further be able to combine them into 'simplified' weathertypes. + # Combined weathertypes correspond to a specific precipitation + # pattern over this region. You can change this to a region of your liking + # to see which types of weathertypes are associated with which precipitation + # patterns. + start_longitude: 9.5 + end_longitude: 17.25 + start_latitude: 46.25 + end_latitude: 49 + + mean_preproc: + extract_time: + *time + extract_region: ® + start_longitude: -15 + end_longitude: 35 + start_latitude: 25 + end_latitude: 65 + +diagnostics: + weathertyping: + description: calculate weathertypes and plot means + variables: + era5_msl_wt: &era5_msl_wt + project: OBS6 + dataset: ERA51 + type: reanaly + frequency: day + mip: day + latestversion: v1 + tier: 3 + start_year: 1958 + end_year: 2014 + short_name: psl + preprocessor: weathertype_preproc + additional_datasets: + *datasets_era5 + + era5_pr_wt: &era5_pr_wt + project: OBS6 + dataset: ERA51 + type: reanaly + frequency: day + mip: day + latestversion: v1 + tier: 3 + start_year: 1958 + end_year: 2014 + short_name: pr + preprocessor: weathertype_preproc_pr + additional_datasets: + *datasets_era5 + + eobs_pr_wt: &eobs_pr_wt + project: OBS + dataset: E-OBS + type: ground + mip: day + version: v29.0e-0.25 + tier: 2 + start_year: 1958 + end_year: 2014 + short_name: pr + preprocessor: weathertype_preproc_pr + additional_datasets: + *datasets_EOBS + + era5_msl_mean: &era5_msl + project: OBS6 + dataset: ERA5 + type: reanaly + frequency: day + mip: day + latestversion: v1 + tier: 3 + start_year: 1958 + end_year: 2014 + short_name: psl + preprocessor: mean_preproc + additional_datasets: + *datasets_era5 + + era5_tp_mean: &era5_prcp + project: OBS6 + dataset: ERA5 + type: reanaly + frequency: day + mip: day + latestversion: v1 + tier: 3 + start_year: 1958 + end_year: 2014 + short_name: pr + preprocessor: mean_preproc + additional_datasets: + *datasets_era5 + + era5_tas_mean: &era5_temp + project: OBS6 + dataset: ERA5 + type: reanaly + frequency: day + mip: day + latestversion: v1 + tier: 3 + start_year: 1958 + end_year: 2014 + short_name: tas + preprocessor: mean_preproc + additional_datasets: + *datasets_era5 + + cmip6_historical_psl_day_wt: &cmip6_historical_wt + project: CMIP6 + activity: CMIP + exp: historical + mip: day + short_name: psl + preprocessor: weathertype_preproc + start_year: 1950 + end_year: 2014 + additional_datasets: + *datasets_psl_cmip6 + + cmip6_historical_psl_day_mean: &cmip6_historical_psl + project: CMIP6 + activity: CMIP + exp: historical + mip: day + short_name: psl + preprocessor: mean_preproc + start_year: 1950 + end_year: 2014 + additional_datasets: + *datasets_psl_cmip6 + + cmip6_historical_prcp_day_mean: &cmip6_historical_prcp + project: CMIP6 + activity: CMIP + exp: historical + mip: day + short_name: pr + preprocessor: mean_preproc + start_year: 1950 + end_year: 2014 + additional_datasets: + *datasets_psl_cmip6 + + cmip6_historical_temp_day_mean: &cmip6_historical_ta + project: CMIP6 + activity: CMIP + exp: historical + mip: day + short_name: tas + preprocessor: mean_preproc + start_year: 1950 + end_year: 2014 + additional_datasets: + *datasets_psl_cmip6 + + scripts: + weathertyping: + correlation_threshold: 0.9 + rmse_threshold: 0.002 + automatic_slwt: true + predefined_slwt: + 1: + - 1 + - 2 + - 19 + 2: + - 3 + - 4 + - 22 + - 21 + 3: + - 5 + - 6 + - 15 + - 16 + 4: + - 7 + - 8 + - 11 + - 18 + 5: + - 9 + - 17 + 6: + - 10 + - 20 + 7: + - 12 + - 13 + - 14 + 8: + - 23 + - 24 + 9: + - 25 + - 26 + 0: + - 27 + plotting: true + script: weathertyping/weathertyping.py From 0c39a5a79a9663c4119a500d59f34d1cfdafe62f Mon Sep 17 00:00:00 2001 From: Thomas Kroissenbrunner Date: Mon, 27 Oct 2025 15:49:01 +0100 Subject: [PATCH 02/57] add reference --- esmvaltool/references/maraun21jgr.bibtex | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 esmvaltool/references/maraun21jgr.bibtex diff --git a/esmvaltool/references/maraun21jgr.bibtex b/esmvaltool/references/maraun21jgr.bibtex new file mode 100644 index 0000000000..613e5d1bb9 --- /dev/null +++ b/esmvaltool/references/maraun21jgr.bibtex @@ -0,0 +1,15 @@ +@article{https://doi.org/10.1029/2020JD032824, + author = {Maraun, Douglas and Truhetz, Heimo and Schaffer, Armin}, + title = {Regional Climate Model Biases, Their Dependence on Synoptic Circulation Biases and the Potential for Bias Adjustment: A Process-Oriented Evaluation of the Austrian Regional Climate Projections}, + journal = {Journal of Geophysical Research: Atmospheres}, + volume = {126}, + number = {6}, + pages = {e2020JD032824}, + keywords = {Austria, bias adjustment, climate model evaluation, large-scale circulation errors, regional climate projections}, + doi = {https://doi.org/10.1029/2020JD032824}, + url = {https://agupubs.onlinelibrary.wiley.com/doi/abs/10.1029/2020JD032824}, + eprint = {https://agupubs.onlinelibrary.wiley.com/doi/pdf/10.1029/2020JD032824}, + note = {e2020JD032824 2020JD032824}, + abstract = {Abstract The Austrian regional climate projections are based on an ensemble of bias adjusted regional climate model simulations. Bias adjustment (BA) improves the usability of climate projections for impact studies, but cannot mitigate fundamental model errors. This argument holds in particular for biases in the temporal dependence, which is strongly influenced by the large-scale circulation. Global climate models (GCMs), underlying regional climate projections, suffer from substantial circulation errors. We therefore, conduct a process-based evaluation of the Austrian climate projections focusing on large-scale circulation errors, their regional imprints and the potential for BA. First, we define nine synoptic weather types and assess how well the considered climate models represent their occurrence and persistence. Second, we assess biases in the overall dry and hot day probabilities, as well as conditional on synoptic weather type occurrence; and biases in the duration of dry and hot spells. Third, we investigate how these biases depend on biases in the occurrence and persistence of relevant synoptic weather types. And fourth, we study how much an overall BA improves these biases. Many GCMs misrepresent the occurrence and persistence of relevant synoptic weather types. These biases have a clear imprint on biases in dry and hot day occurrence and spell durations. BA in many cases helps to greatly reduce biases even in the presence of circulation biases, but may in some cases amplify conditional biases. Persistence biases are especially relevant for the representation of meteorological drought. Biases in the duration of dry spells cannot fully be mitigated by BA.}, + year = {2021} +} From 7bf3acd3ada3cddb5c2c8676d6eb3c74423e586a Mon Sep 17 00:00:00 2001 From: Thomas Kroissenbrunner Date: Tue, 28 Oct 2025 07:55:44 +0100 Subject: [PATCH 03/57] add jones93ijc.bibtex --- esmvaltool/references/jones93ijc.bibtex | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 esmvaltool/references/jones93ijc.bibtex diff --git a/esmvaltool/references/jones93ijc.bibtex b/esmvaltool/references/jones93ijc.bibtex new file mode 100644 index 0000000000..360850ffdf --- /dev/null +++ b/esmvaltool/references/jones93ijc.bibtex @@ -0,0 +1,14 @@ +@article{https://doi.org/10.1002/joc.3370130606, + author = {Jones, P. D. and Hulme, M. and Briffa, K. R.}, + title = {A comparison of Lamb circulation types with an objective classification scheme}, + journal = {International Journal of Climatology}, + volume = {13}, + number = {6}, + pages = {655-663}, + keywords = {Weather types, Temperature, Precipitation, British, Isles}, + doi = {https://doi.org/10.1002/joc.3370130606}, + url = {https://rmets.onlinelibrary.wiley.com/doi/abs/10.1002/joc.3370130606}, + eprint = {https://rmets.onlinelibrary.wiley.com/doi/pdf/10.1002/joc.3370130606}, + abstract = {Abstract An objective scheme, initially developed by Jenkinson and Collison, is used to classify daily circulation types over the British Isles, along the lines of the subjective method devised by Lamb. The scheme uses daily grid-point mean sea-level pressure data for the region. The results of the analysis over the period 1881–1989 are compared with ‘true’ Lamb weather types. The frequencies of objectively developed types are highly correlated with traditional Lamb types, especially so for synoptic (cyclonic and anticyclonic) types, although still good for wind directional types. Comparison of the two classification schemes reveals negligible differences between the correlations of the counts of circulation types and regional temperature and rainfall. The major difference between the two classification schemes is that the decline of the westerlies since 1940 is less evident with the objective scheme.}, + year = {1993} +} From 0c2f5f9595b162bfb9a9a9bf6f18c9a038f90380 Mon Sep 17 00:00:00 2001 From: Thomas Kroissenbrunner Date: Tue, 28 Oct 2025 10:37:48 +0100 Subject: [PATCH 04/57] added dashed unerline --- .../diag_scripts/weathertyping/calc_utils.py | 18 +++++++++ .../diag_scripts/weathertyping/plot_utils.py | 9 ++++- .../weathertyping/weathertyping.py | 4 +- .../diag_scripts/weathertyping/wt_utils.py | 39 +++++++++++++++++++ 4 files changed, 68 insertions(+), 2 deletions(-) diff --git a/esmvaltool/diag_scripts/weathertyping/calc_utils.py b/esmvaltool/diag_scripts/weathertyping/calc_utils.py index de55dfeee2..7d31bc94e5 100644 --- a/esmvaltool/diag_scripts/weathertyping/calc_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/calc_utils.py @@ -47,6 +47,7 @@ def calc_slwt_obs( precipitation patterns over specified area. Args: + ---- cfg (dict): Configuration dictionary from recipe lwt (np.array): Array of Lamb WT cube (iris.cube.Cube): preprocessor cube to keep time coordinate @@ -56,6 +57,7 @@ def calc_slwt_obs( ancestors (list): list of ancestors Returns + ------- np.array: _description_ """ logger.info("Calculating simplified Lamb Weathertypes for %s", dataset) @@ -125,6 +127,7 @@ def calc_const(): Int. J. Climatol., 13: 655-663. https://doi.org/10.1002/joc.3370130606 Returns + ------- tuple: The four constants needed for WT calculation. """ const1 = 1 / np.cos(45 * np.pi / 180) @@ -144,9 +147,11 @@ def calc_westerly_flow(cube: iris.cube.Cube) -> np.array: Int. J. Climatol., 13: 655-663. https://doi.org/10.1002/joc.3370130606 Args: + ---- cube (iris.cube.Cube): Cube of psl data. Returns + ------- np.array: westerly flow """ return 1 / 2 * (cube.data[:, 1, 2] + cube.data[:, 1, 4]) - 1 / 2 * ( @@ -163,10 +168,12 @@ def calc_southerly_flow(cube: iris.cube.Cube, const1: float) -> np.array: Int. J. Climatol., 13: 655-663. https://doi.org/10.1002/joc.3370130606 Args: + ---- cube (iris.cube.Cube): Cube of psl data. const1 (float): const1 Returns + ------- np.array: southerly flow """ return const1 * ( @@ -190,10 +197,12 @@ def calc_resultant_flow( Int. J. Climatol., 13: 655-663. https://doi.org/10.1002/joc.3370130606 Args: + ---- westerly_flow (np.array): westerly flow. southerly_flow (np.array): southerly flow Returns + ------- np.array: resultant flow """ return (southerly_flow**2 + westerly_flow**2) ** (1 / 2) @@ -210,11 +219,13 @@ def calc_westerly_shear_velocity( Int. J. Climatol., 13: 655-663. https://doi.org/10.1002/joc.3370130606 Args: + ---- cube (iris.cube.Cube): cube of psl data const2 (float): const2 const3 (float): const3 Returns + ------- np.array: westerly shear velocity """ return const2 * ( @@ -237,10 +248,12 @@ def calc_southerly_shear_velocity( Int. J. Climatol., 13: 655-663. https://doi.org/10.1002/joc.3370130606 Args: + ---- cube (iris.cube.Cube): cube of psl data const4 (float): const4 Returns + ------- np.array: southerly shear velocity """ return const4 * ( @@ -270,10 +283,12 @@ def calc_total_shear_velocity( Int. J. Climatol., 13: 655-663. https://doi.org/10.1002/joc.3370130606 Args: + ---- westerly_shear_velocity (np.array): westerly shear velocity southerly_shear_velocity (np.array): southerly shear velocity Returns + ------- np.array: total shear velocity """ return westerly_shear_velocity + southerly_shear_velocity @@ -292,6 +307,7 @@ def wt_algorithm(cube: iris.cube.Cube, dataset: str) -> np.array: dataset (str): Name of dataset Returns + ------- np.array: Array of Lamb WT for each day """ # lats and lons corresponding to datapoints @@ -530,6 +546,7 @@ def rmse(subarray1: np.array, subarray2: np.array) -> np.array: subarray2 (np.array): array2 Returns + ------- np.array: rsme array """ return np.sqrt(np.mean((subarray1 - subarray2) ** 2)) @@ -558,6 +575,7 @@ def process_prcp_mean( dataset (str): Name of dataset Returns + ------- list: Selected pairs of WT. This is passed to get_mapping_dict """ logger.info("Calculating corr and rsme matrices for %s", dataset) diff --git a/esmvaltool/diag_scripts/weathertyping/plot_utils.py b/esmvaltool/diag_scripts/weathertyping/plot_utils.py index bbad9c8c24..2601e3f23b 100644 --- a/esmvaltool/diag_scripts/weathertyping/plot_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/plot_utils.py @@ -36,10 +36,12 @@ def generate_grayscale_hex_values(x): """Generate grayscale values for plotting seasonal occurrence. Args: + ---- x (int): number of weathertypes Returns - np.list: array with grescale values as hex + ------- + np.list: array with grayscale values as hex """ grayscale_values = np.linspace(0, 1, x) grayscale_hex = [ @@ -56,6 +58,7 @@ def plot_seasonal_occurrence( """Plot relative monthly/seasonal occurrence of weathertypes. Args: + ---- cfg (dict): Configuration dict from recipe wt_cubes (iris.cube.Cube): Cube with weathertypes data_info (dict): Dictionary with relevant info to dataset @@ -162,6 +165,7 @@ def plot_maps( """Plot maps. Args: + ---- wt (np.array): WT for which statistic was calculated cfg (dict): Configuration dicitonary from recipe cube (iris.cube.Cube): Data to be plotted @@ -275,6 +279,7 @@ def plot_corr_rmse_heatmaps( """Plot heatmaps for correlation and rmse matrices. Args: + ---- cfg (dict): cfg dict from recipe pattern_correlation_matrix (np.array): pattern correlation matrix rmse_matrix (np.array): rmse matrix @@ -353,10 +358,12 @@ def get_colormap(colormap_string: str) -> ListedColormap: """Get colormaps based on string. Args: + ---- colormap_string (str): String to get Colormaps for either psl, tas or precipitation. Returns + ------- ListedColormap: Choosen Colormap """ misc_seq_2_disc = [ diff --git a/esmvaltool/diag_scripts/weathertyping/weathertyping.py b/esmvaltool/diag_scripts/weathertyping/weathertyping.py index 6ea5138932..51d937a296 100644 --- a/esmvaltool/diag_scripts/weathertyping/weathertyping.py +++ b/esmvaltool/diag_scripts/weathertyping/weathertyping.py @@ -13,10 +13,10 @@ calc_lwt_model, calc_lwt_slwt_model, calc_slwt_obs, + plot_means, wt_algorithm, ) from esmvaltool.diag_scripts.weathertyping.plot_utils import ( - plot_means, plot_seasonal_occurrence, ) from esmvaltool.diag_scripts.weathertyping.wt_utils import ( @@ -42,6 +42,7 @@ def run_automatic_slwt(cfg: dict): of the weathertypes. Args: + ---- cfg (dict): Nested dictionary of metadata """ preproc_variables_dict, _, _, work_dir, plotting, _, predefined_slwt = ( @@ -166,6 +167,7 @@ def run_lwt(cfg: dict): and seasonal occurrence of the weathertypes. Args: + ---- cfg (dict): Nested dictionary of metadata """ diff --git a/esmvaltool/diag_scripts/weathertyping/wt_utils.py b/esmvaltool/diag_scripts/weathertyping/wt_utils.py index 53dddfe4e2..eda11a8cbc 100644 --- a/esmvaltool/diag_scripts/weathertyping/wt_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/wt_utils.py @@ -29,11 +29,13 @@ def add_dict_entry(dict_: dict, key: str, value): """Add entry to dictionary. Args: + ---- dict (dict): dictionary to add entry to key (str): key of entry value: value of entry Returns + ------- dict: updated dictionary """ dict_[key] = value @@ -44,9 +46,11 @@ def get_cfg_vars(cfg: dict): """Get list of vars from configuration dict. Args: + ---- cfg (dict): Configuration dict from recipe. Returns + ------- tuple: cfg vars """ preproc_variables_dict = group_metadata( @@ -68,10 +72,12 @@ def load_wt_preprocessors(dataset: str, preproc_variables_dict: dict): """Load preprocessor cubes for calculating Lamb weathertypes. Args: + ---- dataset (str): dataset name preproc_variables_dict (dict): dictionary of preprocessor variables Returns + ------- _type_: list of preprocessor vars for weathertyping """ wt_preproc = iris.load_cube( @@ -91,10 +97,12 @@ def get_ancestors_era5_eobs(dataset: str, preproc_variables_dict: dict): """Get ancestors for ERA5/E-OBS. Args: + ---- dataset (str): dataset name preproc_variables_dict (dict): dictionary of preprocessor variables Returns + ------- _type_: lists of ERA5/E-OBS ancestors """ era5_ancestors = [ @@ -112,10 +120,12 @@ def get_model_output_filepath(dataset: str, value: list): """Generate output filepath for model data. Args: + ---- dataset (str): Model name value (list): Model variables Returns + ------- _type_: Output filepath and preprocessor path for future referencing. """ @@ -134,10 +144,12 @@ def get_preproc_lists(preproc_vars: list): """Put preprocessor variables and paths into list for further use. Args: + ---- preproc_vars (list): List of variables for specific dataset. Returns + ------- _type_: List of preprocessor cubes for mean calculations as well as path to files. """ @@ -159,9 +171,11 @@ def get_preproc_lists_ensemble(preproc_vars: list): """Put preprocessor variables and paths into list for further use. Args: + ---- preproc_vars (list): Variable for specific ensemble member. Returns + ------- _type_: Preprocessor cube for mean calculations as well as path to files. """ @@ -179,9 +193,11 @@ def get_looping_dict(preproc_vars: list): """Put cubes into dictionary for further use. Args: + ---- preproc_vars (list): Values of dataset dictionary Returns + ------- _type_: Dictionary of the form {'var': [preprov_var, preproc_path]} """ preproc, preproc_path = get_preproc_lists(preproc_vars) @@ -200,12 +216,14 @@ def load_wt_files(path: str, only_lwt=False): weathertypes will be loaded. (useful for automatic_slwt = False) Args: + ---- path (str): Path to weathertype data. only_lwt (bool, optional): If True, only Lamb weathertypes will be loaded. Defaults to False. (useful for automatic_slwt = False) Returns + ------- _type_: List of weathertype cubes. """ if not only_lwt: @@ -230,6 +248,7 @@ def get_provenance_record( """Get provenance record. Args: + ---- caption (str): Caption for plots ancestors (list): List of ancestors long_names (list): Variable long names @@ -238,6 +257,7 @@ def get_provenance_record( statistics (bool | list): Types of statistics used. Returns + ------- dict: Provenance dictionary. """ record = { @@ -260,6 +280,7 @@ def log_provenance(filename: str, cfg: dict, provenance_record: dict): """Log provenance. Produces xml file provenance info. Args: + ---- caption (str): Caption of plots. filename (str): Output name of provenance. cfg (dict): Configuration dictionary provided by recipe. @@ -275,9 +296,11 @@ def turn_list_to_mapping_dict(list_: list) -> dict: """Turn list of combined WT into a dictionary for further processing. Args: + ---- list_ (list): List where entries are lists with related WT Returns + ------- dict: Mapping dicitonary keys are simplified WT, values are Lamb WT """ result_dict = {} @@ -296,11 +319,13 @@ def get_mapping_dict(selected_pairs: list) -> dict: """Get mapping dictionary from list of selected pairs. Args: + ---- selected_pairs (list): Selected pairs of WTs based on precipitation patterns over specified area and correlation and RSME thresholds defined in recipe.S Returns + ------- dict: Mapping dicitonary keys are simplified WT, values are Lamb WT """ mapping_array = [] @@ -328,6 +353,7 @@ def write_mapping_dict(work_dir: str, dataset: str, mapping_dict: dict): """Write mapping dictionary to file. Args: + ---- work_dir (str): Working directory dataset (str): Name of dataset mapping_dict (dict): Mapping dictionary @@ -346,9 +372,11 @@ def convert_dict(dict_: dict) -> dict: From {lwt: slwt, ...} format to {slwt: [lwt1, lwt2], ...}. Args: + ---- dict_ (dict): Dict in the {lwt: slwt, ...} format Returns + ------- dict: Dict in the {slwt: [lwt1, lwt2], ...} format """ new_dict = {} @@ -365,9 +393,11 @@ def reverse_convert_dict(dict_: dict) -> dict: From {slwt: [lwt1, lwt2], ...} format to {lwt: slwt, ...}. Args: + ---- original_dict (dict): Dict in the {slwt: [lwt1, lwt2], ...}format Returns + ------- dict: Dict in the format {lwt: slwt, ...} """ new_dict = {} @@ -386,6 +416,7 @@ def write_corr_rmse_to_csv( """Write correlation and rsme matrix to csv files. Args: + ---- cfg (dict): Configuration dictionary from recipe pattern_correlation_matrix (np.array): Correlation matrix rmse_matrix (np.array): RSME matrix @@ -416,12 +447,14 @@ def run_predefined_slwt( """Run predefined slwt mapping. Args: + ---- work_dir (str): Working directory to save mapping dict dataset_name (str): Name of dataset lwt (np.array): lwt array predefined_slwt (dict): Predefined mapping dict Returns + ------- np.array: slwt_era5 array np.array: slwt_eobs array """ @@ -440,6 +473,7 @@ def combine_wt_to_file( """Combine lwt and slwt arrays to one file. Args: + ---- cfg (dict): Configuration dictionary from recipe wt_list (list): List of weathertype arrays cube (iris.cube.Cube): Cube of data to keep time coordinate @@ -484,6 +518,7 @@ def write_lwt_to_file( """Write only lwt to file. Args: + ---- cfg (dict): Configuration dictionary from recipe lwt (np.array): lwt array cube (iris.cube.Cube): Cube to keep time coordinate @@ -512,10 +547,12 @@ def map_lwt_to_slwt(lwt: np.array, mapping_dict: dict) -> np.array: """Map lwt array to slwt array. Args: + ---- lwt (np.array): array of lwt mapping_dict (dict): mapping dictionary in {lwt: slwt, ...} format Returns + ------- np.array: array of slwt """ return np.array([np.int8(mapping_dict.get(value, 0)) for value in lwt]) @@ -525,9 +562,11 @@ def check_mapping_dict_format(mapping_dict: dict) -> dict: """Check format of mapping dict and return in {lwt: slwt, ...} format. Args: + ---- mapping_dict (dict): mapping dict in any format Returns + ------- dict: mapping dict in {lwt: slwt, ...} format """ if isinstance(mapping_dict.get(list(mapping_dict.keys())[0]), list): From 8fecb2c2564af871f57a1b71a506e984f17111ff Mon Sep 17 00:00:00 2001 From: Thomas Kroissenbrunner Date: Tue, 28 Oct 2025 11:15:38 +0100 Subject: [PATCH 05/57] changed docstring format --- .../diag_scripts/weathertyping/calc_utils.py | 209 +++++++++++------ .../diag_scripts/weathertyping/plot_utils.py | 53 +++-- .../weathertyping/weathertyping.py | 53 ++--- .../diag_scripts/weathertyping/wt_utils.py | 222 +++++++++++------- 4 files changed, 324 insertions(+), 213 deletions(-) diff --git a/esmvaltool/diag_scripts/weathertyping/calc_utils.py b/esmvaltool/diag_scripts/weathertyping/calc_utils.py index 7d31bc94e5..724ef9baa5 100644 --- a/esmvaltool/diag_scripts/weathertyping/calc_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/calc_utils.py @@ -48,17 +48,23 @@ def calc_slwt_obs( Args: ---- - cfg (dict): Configuration dictionary from recipe - lwt (np.array): Array of Lamb WT - cube (iris.cube.Cube): preprocessor cube to keep time coordinate - dataset (str): Name of dataset - correlation_thresold (float): correlation_threshold - rmse_threshold (float): rsme_threshold - ancestors (list): list of ancestors + cfg : dict + Configuration dictionary from recipe + lwt : np.array + Array of Lamb WT + cube : iris.cube.Cube + Cube of psl data + dataset : str + Name of dataset + ancestors : list + List of ancestors + timerange : str + Time range for the calculation Returns ------- - np.array: _description_ + np.array + Simplified Lamb Weathertypes """ logger.info("Calculating simplified Lamb Weathertypes for %s", dataset) @@ -128,7 +134,8 @@ def calc_const(): Returns ------- - tuple: The four constants needed for WT calculation. + tuple + The four constants needed for WT calculation. """ const1 = 1 / np.cos(45 * np.pi / 180) const2 = np.sin(45 * np.pi / 180) / np.sin(40 * np.pi / 180) @@ -148,11 +155,13 @@ def calc_westerly_flow(cube: iris.cube.Cube) -> np.array: Args: ---- - cube (iris.cube.Cube): Cube of psl data. + cube : iris.cube.Cube + Cube of psl data. Returns ------- - np.array: westerly flow + np.array + westerly flow """ return 1 / 2 * (cube.data[:, 1, 2] + cube.data[:, 1, 4]) - 1 / 2 * ( cube.data[:, 3, 2] + cube.data[:, 3, 4] @@ -169,12 +178,14 @@ def calc_southerly_flow(cube: iris.cube.Cube, const1: float) -> np.array: Args: ---- - cube (iris.cube.Cube): Cube of psl data. - const1 (float): const1 - + cube : iris.cube.Cube + cube of psl data + const1 : float + const1 Returns ------- - np.array: southerly flow + np.array + southerly flow """ return const1 * ( 1 @@ -198,12 +209,15 @@ def calc_resultant_flow( Args: ---- - westerly_flow (np.array): westerly flow. - southerly_flow (np.array): southerly flow + westerly_flow : np.array + westerly flow + southerly_flow : np.array + southerly flow Returns ------- - np.array: resultant flow + np.array + resultant flow """ return (southerly_flow**2 + westerly_flow**2) ** (1 / 2) @@ -220,13 +234,17 @@ def calc_westerly_shear_velocity( Args: ---- - cube (iris.cube.Cube): cube of psl data - const2 (float): const2 - const3 (float): const3 + cube : iris.cube.Cube + cube of psl data + const2 : float + const2 + const3 : float + const3 Returns ------- - np.array: westerly shear velocity + np.array + westerly shear velocity """ return const2 * ( 1 / 2 * (cube.data[:, 0, 2] + cube.data[:, 0, 4]) @@ -249,12 +267,15 @@ def calc_southerly_shear_velocity( Args: ---- - cube (iris.cube.Cube): cube of psl data - const4 (float): const4 + cube : iris.cube.Cube + cube of psl data + const4 : float + const Returns ------- - np.array: southerly shear velocity + np.array + southerly shear velocity """ return const4 * ( 1 @@ -284,12 +305,15 @@ def calc_total_shear_velocity( Args: ---- - westerly_shear_velocity (np.array): westerly shear velocity - southerly_shear_velocity (np.array): southerly shear velocity + westerly_shear_velocity : np.array + westerly shear velocity + southerly_shear_velocity : np.array + southerly shear velocity Returns ------- - np.array: total shear velocity + np.array + total shear velocity """ return westerly_shear_velocity + southerly_shear_velocity @@ -303,12 +327,16 @@ def wt_algorithm(cube: iris.cube.Cube, dataset: str) -> np.array: Int. J. Climatol., 13: 655-663. https://doi.org/10.1002/joc.3370130606 Args: - cube (iris.cube.Cube): PSL field of dataset - dataset (str): Name of dataset + ---- + cube : iris.cube.Cube + Cube of psl data. + dataset : str + Name of dataset. Returns ------- - np.array: Array of Lamb WT for each day + np.array + Array of Lamb WT for each day """ # lats and lons corresponding to datapoints # 55, 5 -> 1 @@ -442,12 +470,17 @@ def calc_lwt_slwt_model( """Calculate Lamb WT and simplified WT for model data. Args: - cfg (dict): Configuration dicitonary from recipe - cube (iris.cube.Cube): PSL field of dataset - data_info (dict): Dictionary with info to dataset - predefined_slwt (bool | dict): If False, automatic_slwt will be used. - If dict, this mapping dict will be used. - (see recipe option predefined_slwt) + ---- + cfg : dict + Configuration dictionary from recipe + cube : iris.cube.Cube + PSL field of dataset + data_info : dict + Dictionary with info to dataset + predefined_slwt : bool | dict + If False, automatic_slwt will be used. + If dict, this mapping dict will be used. + (see recipe option predefined_slwt) """ work_dir = cfg.get("work_dir") dataset = data_info.get("dataset") @@ -542,12 +575,15 @@ def rmse(subarray1: np.array, subarray2: np.array) -> np.array: """Calculate rsme. Args: - subarray1 (np.array): array1 - subarray2 (np.array): array2 + subarray1 : np.array + First subarray + subarray2 : np.array + Second subarray Returns ------- - np.array: rsme array + np.array + rsme array """ return np.sqrt(np.mean((subarray1 - subarray2) ** 2)) @@ -568,15 +604,23 @@ def process_prcp_mean( further processing and simplifying the WT. Args: - cfg (dict): Configuration dictionary from recipe - data (np.array): Precipitation data - correlation_threshold (float): Correlation threshold - rmse_threshold (float): RMSE threshold - dataset (str): Name of dataset + cfg : dict + Configuration dictionary from recipe + data : np.array + Array of precipitation means for each WT + correlation_threshold : float + correlation threshold + rmse_threshold : float + rsme threshold + dataset : str + Name of dataset + timerange : str + Time range of dataset Returns ------- - list: Selected pairs of WT. This is passed to get_mapping_dict + list + Selected pairs of WT. This is passed to get_mapping_dict """ logger.info("Calculating corr and rsme matrices for %s", dataset) @@ -622,12 +666,14 @@ def calc_wt_means( """Calculate means of fields of each weathertype. Args: - cfg (dict): Configuration dictionary from recipe - cube (iris.cube.Cube): Cube with variable data - wt_cubes (iris.cube.CubeList): List of cubes of lwt, slwt_ERA5 - and slwt_EOBS - data_info (dict): Dictionary with info to dataset - preproc_path (str): Ancestor path + cfg : dict + Configuration dictionary from recipe + cube : iris.cube.Cube + Cube with variable data + wt_cubes : iris.cube.CubeList + List of cubes of lwt, slwt_ERA5 and slwt_EOBS + data_info : dict + Dictionary with info to dataset """ dataset = data_info.get("dataset") var_name = data_info.get("var") @@ -739,12 +785,14 @@ def calc_wt_anomalies( """Calculate anomalies of fields of each weathertype. Args: - cfg (dict): Configuration dictionary from recipe - cube (iris.cube.Cube): Cube with variable data - wt_cubes (iris.cube.CubeList): List of cubes of lwt, slwt_ERA5 - and slwt_EOBS - data_info (dict): Dictionary with info to dataset - preproc_path (str): Ancestor path + cfg : dict + Configuration dictionary from recipe + cube : iris.cube.Cube + Cube with variable data + wt_cubes : iris.cube.CubeList + List of cubes of lwt, slwt_ERA5 and slwt_EOBS + data_info : dict + Dictionary with info to dataset """ work_dir = cfg.get("work_dir") dataset = data_info.get("dataset") @@ -872,12 +920,14 @@ def calc_wt_std( """Calculate standard deviation of fields of each weathertype. Args: - cfg (dict): Configuration dictionary from recipe - cube (iris.cube.Cube): Cube with variable data - wt_cubes (iris.cube.CubeList): List of cubes of lwt, slwt_ERA5 - and slwt_EOBS - data_info (dict): Dictionary with info to dataset - preproc_path (str): Ancestor path + cfg : dict + Configuration dictionary from recipe + cube : iris.cube.Cube + Cube with variable data + wt_cubes : iris.cube.CubeList + List of cubes of lwt, slwt_ERA5 and slwt_EOBS + data_info : dict + Dictionary with info to dataset """ work_dir = cfg.get("work_dir") dataset = data_info.get("dataset") @@ -998,10 +1048,15 @@ def calc_lwt_model( """Calculate lwt for model data. Args: - cfg (dict): Configuration dictionary from recipe - cube (iris.cube.Cube): Cube to keep time coordinate - dataset (str): Name of dataset - data_info (dict): Dictionary with info to dataset + ---- + cfg : dict + Configuration dictionary from recipe + cube : iris.cube.Cube + Cube to keep time coordinate + dataset : str + Name of dataset + data_info : dict + Dictionary with info to dataset """ work_dir = cfg.get("work_dir") dataset = data_info.get("dataset") @@ -1072,13 +1127,17 @@ def plot_means( """Wrapper function to plot various means/std/anomalies. Args: - cfg (dict): cfg dictionary provided by recipe - preproc_var (np.array): variable to be plotted - wt_cubes (iris.cube.Cube): list of wt cubes - data_info (dict): dictionary with info to dataset - only_lwt (bool, optional): If True, - only Lamb weathertypes will be loaded. Defaults to False. - (useful for automatic_slwt = False) + cfg : dict + Configuration dictionary from recipe + preproc_var : np.array + Preprocessed variable cube + wt_cubes : iris.cube.Cube + List of cubes of lwt, slwt_ERA5 and slwt_EOBS + data_info : dict + Dictionary with info to dataset + only_lwt : bool + If True, only lwt means are calculated. + If False, lwt, slwt_ERA5 and slwt_EOBS means are calculated. """ if not only_lwt: data_info["wt_string"] = "lwt" diff --git a/esmvaltool/diag_scripts/weathertyping/plot_utils.py b/esmvaltool/diag_scripts/weathertyping/plot_utils.py index 2601e3f23b..7d30f9332b 100644 --- a/esmvaltool/diag_scripts/weathertyping/plot_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/plot_utils.py @@ -37,11 +37,13 @@ def generate_grayscale_hex_values(x): Args: ---- - x (int): number of weathertypes + x : int + number of weathertypes Returns ------- - np.list: array with grayscale values as hex + np.list + array with grayscale values as hex """ grayscale_values = np.linspace(0, 1, x) grayscale_hex = [ @@ -59,9 +61,12 @@ def plot_seasonal_occurrence( Args: ---- - cfg (dict): Configuration dict from recipe - wt_cubes (iris.cube.Cube): Cube with weathertypes - data_info (dict): Dictionary with relevant info to dataset + cfg : dict + Configuration dictionary from recipe + wt_cubes : iris.cube.Cube + Cube with weathertypes + data_info : dict + Dictionary with relevant info to dataset """ dataset_name = data_info.get("dataset") timerange = data_info.get("timerange") @@ -166,11 +171,16 @@ def plot_maps( Args: ---- - wt (np.array): WT for which statistic was calculated - cfg (dict): Configuration dicitonary from recipe - cube (iris.cube.Cube): Data to be plotted - data_info (dict): Dictionary with info to dataset - mode (str): Statistics that is used + wt : np.array + weathertype number + cfg : dict + Configuration dicitonary from recipe + cube : iris.cube.Cube + Data to be plotted + data_info : dict + Dictionary with info to dataset + mode : str + Statistics that is used """ dataset = data_info.get("dataset") var_name = data_info.get("var") @@ -280,11 +290,16 @@ def plot_corr_rmse_heatmaps( Args: ---- - cfg (dict): cfg dict from recipe - pattern_correlation_matrix (np.array): pattern correlation matrix - rmse_matrix (np.array): rmse matrix - dataset (str): string of dataset - timerange (str): string of timerange + cfg : dict + Configuration dictionary from recipe + pattern_correlation_matrix : np.array + pattern correlation matrix + rmse_matrix : np.array + rmse matrix + dataset : str + string of dataset + timerange : str + string of timerange """ output_path = f"{cfg.get('plot_dir')}/heatmaps" @@ -359,12 +374,14 @@ def get_colormap(colormap_string: str) -> ListedColormap: Args: ---- - colormap_string (str): String to get Colormaps for either - psl, tas or precipitation. + colormap_string : str + String to get Colormaps for either + psl, tas or precipitation. Returns ------- - ListedColormap: Choosen Colormap + ListedColormap + Choosen Colormap """ misc_seq_2_disc = [ (230 / 255, 240 / 255, 240 / 255), diff --git a/esmvaltool/diag_scripts/weathertyping/weathertyping.py b/esmvaltool/diag_scripts/weathertyping/weathertyping.py index 51d937a296..841b8b9b06 100644 --- a/esmvaltool/diag_scripts/weathertyping/weathertyping.py +++ b/esmvaltool/diag_scripts/weathertyping/weathertyping.py @@ -20,7 +20,6 @@ plot_seasonal_occurrence, ) from esmvaltool.diag_scripts.weathertyping.wt_utils import ( - add_dict_entry, combine_wt_to_file, get_ancestors_era5_eobs, get_cfg_vars, @@ -43,7 +42,8 @@ def run_automatic_slwt(cfg: dict): Args: ---- - cfg (dict): Nested dictionary of metadata + cfg : dict + Nested dictionary of metadata """ preproc_variables_dict, _, _, work_dir, plotting, _, predefined_slwt = ( get_cfg_vars(cfg) @@ -102,8 +102,8 @@ def run_automatic_slwt(cfg: dict): if plotting: # plot means for var_name, var_data in var_dict.items(): - add_dict_entry(data_info, "var", var_name) - add_dict_entry(data_info, "preproc_path", var_data[1]) + data_info["var"] = var_name + data_info["preproc_path"] = var_data[1] plot_means(cfg, var_data[0], wt_cubes, data_info) plot_seasonal_occurrence(cfg, wt_cubes, data_info) @@ -112,14 +112,11 @@ def run_automatic_slwt(cfg: dict): continue for ensemble_var in dataset_vars: if ensemble_var.get("preprocessor") == "weathertype_preproc": - add_dict_entry( - data_info, "ensemble", ensemble_var.get("ensemble", "") - ) - add_dict_entry( - data_info, "driver", ensemble_var.get("driver", "") - ) + data_info["ensemble"] = ensemble_var.get("ensemble", "") + data_info["driver"] = ensemble_var.get("driver", "") + if data_info["driver"] != "": - data_info["driver"] = "_" + {data_info["driver"]} + data_info["driver"] = "_" + data_info["driver"] wt_preproc = iris.load_cube(ensemble_var.get("filename")) @@ -153,10 +150,8 @@ def run_automatic_slwt(cfg: dict): # plot means if plotting: for var_name, var_data in var_dict.items(): - add_dict_entry(data_info, "var", var_name) - add_dict_entry( - data_info, "preproc_path", var_data[1] - ) + data_info["var"] = var_name + data_info["preproc_path"] = var_data[1] plot_means(cfg, var_data[0], wt_cubes, data_info) plot_seasonal_occurrence(cfg, wt_cubes, data_info) @@ -168,7 +163,8 @@ def run_lwt(cfg: dict): Args: ---- - cfg (dict): Nested dictionary of metadata + cfg : dict + Nested dictionary of metadata """ preproc_variables_dict, _, _, work_dir, plotting, _, _ = get_cfg_vars(cfg) @@ -217,8 +213,8 @@ def run_lwt(cfg: dict): if plotting: # plot means for var_name, var_data in var_dict.items(): - add_dict_entry(data_info, "var", var_name) - add_dict_entry(data_info, "preproc_path", var_data[1]) + data_info["var"] = var_name + data_info["preproc_path"] = var_data[1] plot_means( cfg, var_data[0], wt_cubes, data_info, only_lwt=True @@ -229,12 +225,9 @@ def run_lwt(cfg: dict): continue for ensemble_var in dataset_vars: if ensemble_var.get("preprocessor") == "weathertype_preproc": - add_dict_entry( - data_info, "ensemble", ensemble_var.get("ensemble", "") - ) - add_dict_entry( - data_info, "driver", ensemble_var.get("driver", "") - ) + data_info["ensemble"] = ensemble_var.get("ensemble", "") + data_info["driver"] = ensemble_var.get("driver", "") + if data_info["driver"] != "": data_info["driver"] = "_" + {data_info["driver"]} @@ -269,10 +262,8 @@ def run_lwt(cfg: dict): if plotting: # plot means for var_name, var_data in var_dict.items(): - add_dict_entry(data_info, "var", var_name) - add_dict_entry( - data_info, "preproc_path", var_data[1] - ) + data_info["var"] = var_name + data_info["preproc_path"] = var_data[1] plot_means( cfg, @@ -288,10 +279,12 @@ def run_my_diagnostic(cfg: dict): """Run the weathertyping diagnostic. Arguments: - cfg - nested dictionary of metadata + cfg : dict + nested dictionary of metadata Returns - string; runs the user diagnostic + string + runs the user diagnostic """ # assemble the data dictionary keyed by dataset name diff --git a/esmvaltool/diag_scripts/weathertyping/wt_utils.py b/esmvaltool/diag_scripts/weathertyping/wt_utils.py index eda11a8cbc..4f905dff88 100644 --- a/esmvaltool/diag_scripts/weathertyping/wt_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/wt_utils.py @@ -25,33 +25,18 @@ warnings.filterwarnings("ignore", ".*Collapsing a non-contiguous coordinate*") -def add_dict_entry(dict_: dict, key: str, value): - """Add entry to dictionary. - - Args: - ---- - dict (dict): dictionary to add entry to - key (str): key of entry - value: value of entry - - Returns - ------- - dict: updated dictionary - """ - dict_[key] = value - return dict_ - - def get_cfg_vars(cfg: dict): """Get list of vars from configuration dict. Args: ---- - cfg (dict): Configuration dict from recipe. + cfg : dict + Configuration dict from recipe. Returns ------- - tuple: cfg vars + tuple + cfg vars """ preproc_variables_dict = group_metadata( cfg.get("input_data").values(), "dataset" @@ -73,12 +58,15 @@ def load_wt_preprocessors(dataset: str, preproc_variables_dict: dict): Args: ---- - dataset (str): dataset name - preproc_variables_dict (dict): dictionary of preprocessor variables + dataset : str + dataset name + preproc_variables_dict : dict + dictionary of preprocessor variables Returns ------- - _type_: list of preprocessor vars for weathertyping + list + list of preprocessor vars for weathertyping """ wt_preproc = iris.load_cube( preproc_variables_dict.get(dataset)[0].get("filename") @@ -98,12 +86,15 @@ def get_ancestors_era5_eobs(dataset: str, preproc_variables_dict: dict): Args: ---- - dataset (str): dataset name - preproc_variables_dict (dict): dictionary of preprocessor variables + dataset : str + dataset name + preproc_variables_dict : dict + dictionary of preprocessor variables Returns ------- - _type_: lists of ERA5/E-OBS ancestors + tuple(list, list) + lists of ERA5/E-OBS ancestors """ era5_ancestors = [ preproc_variables_dict.get(dataset)[0].get("filename"), @@ -121,13 +112,16 @@ def get_model_output_filepath(dataset: str, value: list): Args: ---- - dataset (str): Model name - value (list): Model variables + dataset : str + dataset name + value : dict + Model variables Returns ------- - _type_: Output filepath and preprocessor path for - future referencing. + tuple(str, str) + Output filepath and preprocessor path for + future referencing. """ timerange = value.get("timerange").replace("/", "-") experiment = value.get("exp") @@ -145,13 +139,15 @@ def get_preproc_lists(preproc_vars: list): Args: ---- - preproc_vars (list): List of variables for specific - dataset. + preproc_vars : list + List of variables for specific + dataset Returns ------- - _type_: List of preprocessor cubes for mean calculations - as well as path to files. + tuple(list, list) + List of preprocessor cubes for mean calculations + as well as path to files. """ preproc_path_psl = preproc_vars[-3].get("filename") preproc_path_prcp = preproc_vars[-2].get("filename") @@ -172,12 +168,14 @@ def get_preproc_lists_ensemble(preproc_vars: list): Args: ---- - preproc_vars (list): Variable for specific ensemble member. + preproc_vars : list + Variable for specific ensemble member. Returns ------- - _type_: Preprocessor cube for mean calculations - as well as path to files. + tuple(iris.cube.Cube, str) + Preprocessor cube for mean calculations + as well as path to files. """ preproc_path = preproc_vars.get("filename") @@ -194,11 +192,12 @@ def get_looping_dict(preproc_vars: list): Args: ---- - preproc_vars (list): Values of dataset dictionary + preproc_vars : list + Values of dataset dictionary Returns ------- - _type_: Dictionary of the form {'var': [preprov_var, preproc_path]} + Dictionary of the form {'var': [preproc_var, preproc_path]} """ preproc, preproc_path = get_preproc_lists(preproc_vars) dict_ = { @@ -217,14 +216,16 @@ def load_wt_files(path: str, only_lwt=False): Args: ---- - path (str): Path to weathertype data. - only_lwt (bool, optional): If True, - only Lamb weathertypes will be loaded. Defaults to False. - (useful for automatic_slwt = False) + path : str + Path to weathertype data. + only_lwt : bool, optional + If True, only Lamb weathertypes will be loaded. + Defaults to False. Returns ------- - _type_: List of weathertype cubes. + list(iris.cube.Cube) + List of weathertype cubes. """ if not only_lwt: lwt_cube = iris.load_cube(path, "lwt") @@ -249,16 +250,21 @@ def get_provenance_record( Args: ---- - caption (str): Caption for plots - ancestors (list): List of ancestors - long_names (list): Variable long names - plot_types (bool | list): Type of plot. Can be false - if output is not a plot. - statistics (bool | list): Types of statistics used. + caption : str + Caption for plots + ancestors : list + List of ancestors + long_names : list + Variable long names + plot_types : bool | list + Type of plot. Can be false if output is not a plot. + statistics : bool | list + Types of statistics used. Returns ------- - dict: Provenance dictionary. + dict + Provenance dictionary. """ record = { "caption": caption, @@ -281,10 +287,12 @@ def log_provenance(filename: str, cfg: dict, provenance_record: dict): Args: ---- - caption (str): Caption of plots. - filename (str): Output name of provenance. - cfg (dict): Configuration dictionary provided by recipe. - provenance_record (dict): Provenance record dictionary. + filename : str + Output name of provenance. + cfg : dict + Configuration dictionary provided by recipe. + provenance_record : dict + Provenance record dictionary. """ with ProvenanceLogger(cfg) as provenance_logger: provenance_logger.log(filename, provenance_record) @@ -297,11 +305,13 @@ def turn_list_to_mapping_dict(list_: list) -> dict: Args: ---- - list_ (list): List where entries are lists with related WT + list_ : list + List where entries are lists with related WT Returns ------- - dict: Mapping dicitonary keys are simplified WT, values are Lamb WT + dict + Mapping dictionary keys are simplified WT, values are Lamb WT """ result_dict = {} @@ -320,13 +330,15 @@ def get_mapping_dict(selected_pairs: list) -> dict: Args: ---- - selected_pairs (list): Selected pairs of WTs based on - precipitation patterns over specified area and - correlation and RSME thresholds defined in recipe.S + selected_pairs : list + List of selected pairs of related WT based on + precipitation patterns over specified area and + correlation and RSME thresholds defined in recipe Returns ------- - dict: Mapping dicitonary keys are simplified WT, values are Lamb WT + dict + Mapping dicitonary keys are simplified WT, values are Lamb WT """ mapping_array = [] @@ -354,9 +366,12 @@ def write_mapping_dict(work_dir: str, dataset: str, mapping_dict: dict): Args: ---- - work_dir (str): Working directory - dataset (str): Name of dataset - mapping_dict (dict): Mapping dictionary + work_dir : str + Working directory to save mapping dict + dataset : str + Name of dataset + mapping_dict : dict + Mapping dictionary in {lwt: slwt, ...} format """ mapping_dict_reformat = convert_dict(mapping_dict) @@ -373,11 +388,13 @@ def convert_dict(dict_: dict) -> dict: Args: ---- - dict_ (dict): Dict in the {lwt: slwt, ...} format + dict_ : dict + Dict in the {lwt: slwt, ...} format Returns ------- - dict: Dict in the {slwt: [lwt1, lwt2], ...} format + dict + Dict in the {slwt: [lwt1, lwt2], ...} format """ new_dict = {} for dataset, value in dict_.items(): @@ -394,11 +411,13 @@ def reverse_convert_dict(dict_: dict) -> dict: Args: ---- - original_dict (dict): Dict in the {slwt: [lwt1, lwt2], ...}format + original_dict : dict + Dict in the {slwt: [lwt1, lwt2], ...} format Returns ------- - dict: Dict in the format {lwt: slwt, ...} + dict + Dict in the format {lwt: slwt, ...} """ new_dict = {} for key, value_list in dict_.items(): @@ -417,10 +436,14 @@ def write_corr_rmse_to_csv( Args: ---- - cfg (dict): Configuration dictionary from recipe - pattern_correlation_matrix (np.array): Correlation matrix - rmse_matrix (np.array): RSME matrix - dataset (str): Name of dataset + cfg : dict + Configuration dictionary from recipe + pattern_correlation_matrix : np.array + Correlation matrix + rmse_matrix : np.array + RSME matrix + dataset : str + Name of dataset """ logger.info("Writing corr and rsme matrices for %s", dataset) @@ -448,15 +471,21 @@ def run_predefined_slwt( Args: ---- - work_dir (str): Working directory to save mapping dict - dataset_name (str): Name of dataset - lwt (np.array): lwt array - predefined_slwt (dict): Predefined mapping dict + work_dir : str + Working directory to save mapping dict + dataset_name : str + Name of dataset + lwt : np.array + lwt array + predefined_slwt : dict + Mapping dictionary in {lwt: slwt, ...} format Returns ------- - np.array: slwt_era5 array - np.array: slwt_eobs array + np.array + slwt_era5 array + np.array + slwt_eobs array """ predefined_slwt = check_mapping_dict_format(predefined_slwt) write_mapping_dict(work_dir, dataset_name, predefined_slwt) @@ -474,10 +503,14 @@ def combine_wt_to_file( Args: ---- - cfg (dict): Configuration dictionary from recipe - wt_list (list): List of weathertype arrays - cube (iris.cube.Cube): Cube of data to keep time coordinate - file_name (str): Name of output file + cfg : dict + Configuration dictionary from recipe + wt_list : list + List of weathertype arrays + cube : iris.cube.Cube + Cube of data to keep time coordinate + file_name : str + Name of output file """ lwt = wt_list[0] slwt_era5 = wt_list[1] @@ -519,10 +552,14 @@ def write_lwt_to_file( Args: ---- - cfg (dict): Configuration dictionary from recipe - lwt (np.array): lwt array - cube (iris.cube.Cube): Cube to keep time coordinate - file_name (str): Output filename + cfg : dict + Configuration dictionary from recipe + lwt : np.array + lwt array + cube : iris.cube.Cube + Cube of data to keep time coordinate + file_name : str + Name of output file """ logger.info("Writing Lamb Weathertype to %s", file_name) @@ -548,12 +585,15 @@ def map_lwt_to_slwt(lwt: np.array, mapping_dict: dict) -> np.array: Args: ---- - lwt (np.array): array of lwt - mapping_dict (dict): mapping dictionary in {lwt: slwt, ...} format + lwt : np.array + lwt array + mapping_dict : dict + Mapping dictionary in {lwt: slwt, ...} format Returns ------- - np.array: array of slwt + np.array + array of slwt """ return np.array([np.int8(mapping_dict.get(value, 0)) for value in lwt]) @@ -563,11 +603,13 @@ def check_mapping_dict_format(mapping_dict: dict) -> dict: Args: ---- - mapping_dict (dict): mapping dict in any format + mapping_dict : dict + mapping dict in any format Returns ------- - dict: mapping dict in {lwt: slwt, ...} format + dict + mapping dict in {lwt: slwt, ...} format """ if isinstance(mapping_dict.get(list(mapping_dict.keys())[0]), list): dict_ = reverse_convert_dict(mapping_dict) From ce833be3bf893767e828e8dd59c6bab18493c824 Mon Sep 17 00:00:00 2001 From: Thomas Kroissenbrunner Date: Tue, 4 Nov 2025 09:08:45 +0100 Subject: [PATCH 06/57] using less locals, refactoring weathertyping.py --- .../diag_scripts/weathertyping/calc_utils.py | 195 ++++---- .../weathertyping/weathertyping.py | 439 ++++++++++-------- 2 files changed, 348 insertions(+), 286 deletions(-) diff --git a/esmvaltool/diag_scripts/weathertyping/calc_utils.py b/esmvaltool/diag_scripts/weathertyping/calc_utils.py index 724ef9baa5..85c5e3c8e9 100644 --- a/esmvaltool/diag_scripts/weathertyping/calc_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/calc_utils.py @@ -68,11 +68,6 @@ def calc_slwt_obs( """ logger.info("Calculating simplified Lamb Weathertypes for %s", dataset) - work_dir = cfg.get("work_dir") - correlation_threshold = cfg.get("correlation_threshold") - rmse_threshold = cfg.get("rmse_threshold") - tcoord = cube.coord("time") - wt_data_prcp = [] for wt_ in range(1, 28): target_indices = np.where(lwt == wt_) @@ -85,7 +80,8 @@ def calc_slwt_obs( ) continue dates = [ - tcoord.units.num2date(tcoord.points[i]) for i in target_indices + cube.coord("time").units.num2date(cube.coord("time").points[i]) + for i in target_indices ] if dataset == "E-OBS": extracted_cube = cube[target_indices] @@ -98,27 +94,29 @@ def calc_slwt_obs( selected_pairs = process_prcp_mean( cfg, wt_data_prcp, - correlation_threshold, - rmse_threshold, dataset, timerange, ) with open( - f"{work_dir}/wt_selected_pairs_{dataset}.json", "w", encoding="utf-8" + f"{cfg.get('work_dir')}/wt_selected_pairs_{dataset}.json", + "w", + encoding="utf-8", ) as file: json.dump(selected_pairs, file) mapping_dict = get_mapping_dict(selected_pairs) - write_mapping_dict(work_dir, dataset, mapping_dict) + write_mapping_dict(cfg.get("work_dir"), dataset, mapping_dict) provenance_record = get_provenance_record( "Lamb Weathertypes", ancestors, ["Lamb Weathertypes"], False, False ) log_provenance( - f"{work_dir}/wt_selected_pairs_{dataset}", cfg, provenance_record + f"{cfg.get('work_dir')}/wt_selected_pairs_{dataset}", + cfg, + provenance_record, ) return map_lwt_to_slwt(lwt, mapping_dict) @@ -482,36 +480,37 @@ def calc_lwt_slwt_model( If dict, this mapping dict will be used. (see recipe option predefined_slwt) """ - work_dir = cfg.get("work_dir") - dataset = data_info.get("dataset") - preproc_path = data_info.get("preproc_path") - output_file_path = data_info.get("output_file_path") - ensemble = data_info.get("ensemble", "") - timerange = data_info.get("timerange") driver = data_info.get("driver", "") if driver != "": driver = f"_{driver}" - if not os.path.exists(f"{work_dir}/{output_file_path}"): - os.makedirs(f"{work_dir}/{output_file_path}") + if not os.path.exists( + f"{cfg.get('work_dir')}/{data_info.get('output_file_path')}" + ): + os.makedirs( + f"{cfg.get('work_dir')}/{data_info.get('output_file_path')}" + ) - lwt = wt_algorithm(cube, dataset) + lwt = wt_algorithm(cube, data_info.get("dataset")) - tcoord = cube.coord("time") - time_points = tcoord.units.num2date(tcoord.points) + time_points = cube.coord("time").units.num2date(cube.coord("time").points) logger.info( - "Calculating simplified Lamb Weathertypes for %s %s", dataset, ensemble + "Calculating simplified Lamb Weathertypes for %s %s", + data_info.get("dataset"), + data_info.get("ensemble", ""), ) if not predefined_slwt: with open( - f"{work_dir}/wt_mapping_dict_ERA5.json", encoding="utf-8" + f"{cfg.get('work_dir')}/wt_mapping_dict_ERA5.json", + encoding="utf-8", ) as file: mapping_dict_era5_f = json.load(file) with open( - f"{work_dir}/wt_mapping_dict_E-OBS.json", encoding="utf-8" + f"{cfg.get('work_dir')}/wt_mapping_dict_E-OBS.json", + encoding="utf-8", ) as file: mapping_dict_eobs_f = json.load(file) mapping_dict_era5 = reverse_convert_dict(mapping_dict_era5_f) @@ -521,8 +520,8 @@ def calc_lwt_slwt_model( slwt_eobs = map_lwt_to_slwt(lwt, mapping_dict_eobs) else: predefined_slwt = check_mapping_dict_format(predefined_slwt) - write_mapping_dict(work_dir, "ERA5", predefined_slwt) - write_mapping_dict(work_dir, "E-OBS", predefined_slwt) + write_mapping_dict(cfg.get("work_dir"), "ERA5", predefined_slwt) + write_mapping_dict(cfg.get("work_dir"), "E-OBS", predefined_slwt) slwt_era5 = map_lwt_to_slwt(lwt, predefined_slwt) slwt_eobs = map_lwt_to_slwt(lwt, predefined_slwt) @@ -531,14 +530,14 @@ def calc_lwt_slwt_model( wt_cube.append(iris.cube.Cube(slwt_era5, long_name="slwt_era5")) wt_cube.append(iris.cube.Cube(slwt_eobs, long_name="slwt_eobs")) - wt_cube[0].add_dim_coord(tcoord, 0) - wt_cube[1].add_dim_coord(tcoord, 0) - wt_cube[2].add_dim_coord(tcoord, 0) + wt_cube[0].add_dim_coord(cube.coord("time"), 0) + wt_cube[1].add_dim_coord(cube.coord("time"), 0) + wt_cube[2].add_dim_coord(cube.coord("time"), 0) iris.save( wt_cube, - f"{work_dir}/{output_file_path}/{dataset}{driver}_" - f"{ensemble}_{timerange}.nc", + f"{cfg.get('work_dir')}/{data_info.get('output_file_path')}/{data_info.get('dataset')}{driver}_" + f"{data_info.get('ensemble', '')}_{data_info.get('timerange')}.nc", ) # write to csv file @@ -550,22 +549,22 @@ def calc_lwt_slwt_model( } df = pd.DataFrame(data=d) df.to_csv( - f"{work_dir}/{output_file_path}/{dataset}{driver}_{ensemble}_" - f"{timerange}.csv", + f"{cfg.get('work_dir')}/{data_info.get('output_file_path')}/{data_info.get('dataset')}{driver}_{data_info.get('ensemble', '')}_" + f"{data_info.get('timerange')}.csv", index=False, ) ancestors = [ - preproc_path, - f"{work_dir}/wt_mapping_dict_ERA5.json", - f"{work_dir}/wt_mapping_dict_E-OBS.json", + data_info.get("preproc_path"), + f"{cfg.get('work_dir')}/wt_mapping_dict_ERA5.json", + f"{cfg.get('work_dir')}/wt_mapping_dict_E-OBS.json", ] provenance_record = get_provenance_record( "Lamb Weathertypes", ancestors, ["Lamb Weathertypes"], False, False ) log_provenance( - f"{work_dir}/{output_file_path}/{dataset}_{ensemble}", + f"{cfg.get('work_dir')}/{data_info.get('output_file_path')}/{data_info.get('dataset')}_{data_info.get('ensemble', '')}", cfg, provenance_record, ) @@ -591,8 +590,6 @@ def rmse(subarray1: np.array, subarray2: np.array) -> np.array: def process_prcp_mean( cfg: dict, data: np.array, - correlation_threshold: float, - rmse_threshold: float, dataset: str, timerange: str, ) -> list: @@ -633,10 +630,9 @@ def process_prcp_mean( for j in range(i + 1, n): rmse_matrix[i][j] = rmse(data[i], data[j]) rmse_matrix[j][i] = rmse_matrix[i][j] - if ( - pattern_correlation_matrix[i][j] >= correlation_threshold - and rmse_matrix[i][j] <= rmse_threshold - ): + if pattern_correlation_matrix[i][j] >= cfg.get( + "correlation_threshold" + ) and rmse_matrix[i][j] <= cfg.get("rmse_threshold"): selected_pairs.append( ( (i + 1, j + 1), @@ -675,19 +671,18 @@ def calc_wt_means( data_info : dict Dictionary with info to dataset """ - dataset = data_info.get("dataset") var_name = data_info.get("var") wt_string = data_info.get("wt_string") - preproc_path = data_info.get("preproc_path") - ensemble = data_info.get("ensemble") - timerange = data_info.get("timerange") driver = data_info.get("driver", "") if driver != "": driver = f"_{driver}" - logger.info("Calculating %s %s means for %s", dataset, var_name, wt_string) - - work_dir = cfg.get("work_dir") + logger.info( + "Calculating %s %s means for %s", + data_info.get("dataset"), + var_name, + wt_string, + ) num_slwt = 0 target_indices = [] @@ -722,7 +717,7 @@ def calc_wt_means( for dataset %s!", wt_string, wt, - dataset, + data_info.get("dataset"), ) continue dates = [ @@ -741,7 +736,7 @@ def calc_wt_means( "calc_wt_means - CAUTION: Skipped lwt %s \ for dataset %s!", wt, - dataset, + data_info.get("dataset"), ) continue dates = [ @@ -755,7 +750,10 @@ def calc_wt_means( else: logger.info("WT_STRING NOT SUPPORTED.") - ancestors = [f"{preproc_path}", f"{work_dir}/ERA5.nc"] + ancestors = [ + f"{data_info.get('preproc_path')}", + f"{cfg.get('work_dir')}/ERA5.nc", + ] provenance_record = get_provenance_record( f"{var_name} means for \ {wt_string}", @@ -769,8 +767,8 @@ def calc_wt_means( log_provenance( f"{local_path}/{wt_string}_{wt}{driver}_" - f"{dataset}_{ensemble}" - f"_{var_name}_mean_{timerange}", + f"{data_info.get('dataset')}_{data_info.get('ensemble', '')}" + f"_{var_name}_mean_{data_info.get('timerange')}", cfg, provenance_record, ) @@ -794,16 +792,14 @@ def calc_wt_anomalies( data_info : dict Dictionary with info to dataset """ - work_dir = cfg.get("work_dir") - dataset = data_info.get("dataset") var_name = data_info.get("var_name") wt_string = data_info.get("wt_string") - preproc_path = data_info.get("preproc_path") - ensemble = data_info.get("ensemble") - timerange = data_info.get("timerange") logger.info( - "Calculating %s %s anomalies for %s", dataset, var_name, wt_string + "Calculating %s %s anomalies for %s", + data_info.get("dataset"), + var_name, + wt_string, ) target_indices = [] @@ -846,7 +842,7 @@ def calc_wt_anomalies( "calc_wt_anomalies - CAUTION: Skipped wt %s \ for dataset %s!", wt, - dataset, + data_info.get("dataset"), ) continue dates = [ @@ -871,7 +867,7 @@ def calc_wt_anomalies( "calc_wt_anomalies - CAUTION: Skipped wt %s \ for dataset %s!", wt, - dataset, + data_info.get("dataset"), ) continue dates = [ @@ -891,7 +887,10 @@ def calc_wt_anomalies( else: logger.info("WT_STRING NOT SUPPORTED.") - ancestors = [f"{preproc_path}", f"{work_dir}/ERA5.nc"] + ancestors = [ + f"{data_info.get('preproc_path')}", + f"{cfg.get('work_dir')}/ERA5.nc", + ] provenance_record = get_provenance_record( f"{var_name} anomaly for \ {wt_string}", @@ -904,8 +903,8 @@ def calc_wt_anomalies( local_path = f"{cfg.get('plot_dir')}/anomaly" log_provenance( - f"{local_path}/{wt_string}_{wt}_{dataset}_{ensemble}" - f"_{var_name}_anomaly__{timerange}", + f"{local_path}/{wt_string}_{wt}_{data_info.get('dataset')}_{data_info.get('ensemble', '')}" + f"_{var_name}_anomaly__{data_info.get('timerange')}", cfg, provenance_record, ) @@ -929,17 +928,12 @@ def calc_wt_std( data_info : dict Dictionary with info to dataset """ - work_dir = cfg.get("work_dir") - dataset = data_info.get("dataset") var_name = data_info.get("var_name") wt_string = data_info.get("wt_string") - preproc_path = data_info.get("preproc_path") - ensemble = data_info.get("ensemble") - timerange = data_info.get("timerange") logger.info( "Calculating %s %s standard deviation for %s", - dataset, + data_info.get("dataset"), var_name, wt_string, ) @@ -984,7 +978,7 @@ def calc_wt_std( "calc_slwt_obs - CAUTION: Skipped wt %s \ for dataset %s!", wt, - dataset, + data_info.get("dataset"), ) continue dates = [ @@ -1005,7 +999,7 @@ def calc_wt_std( "calc_wt_std - CAUTION: Skipped wt %s \ for dataset %s!", wt, - dataset, + data_info.get("dataset"), ) continue dates = [ @@ -1021,7 +1015,10 @@ def calc_wt_std( else: logger.info("WT_STRING NOT SUPPORTED.") - ancestors = [f"{preproc_path}", f"{work_dir}/ERA5.nc"] + ancestors = [ + f"{data_info.get('preproc_path')}", + f"{cfg.get('work_dir')}/ERA5.nc", + ] provenance_record = get_provenance_record( f"{var_name} standard \ deviation for \ @@ -1035,16 +1032,14 @@ def calc_wt_std( local_path = f"{cfg.get('plot_dir')}/stddev" log_provenance( - f"{local_path}/{wt_string}_{wt}_{dataset}_{ensemble}" - f"_{var_name}_stddev_{timerange}", + f"{local_path}/{wt_string}_{wt}_{data_info.get('dataset')}_{data_info.get('ensemble', '')}" + f"_{var_name}_stddev_{data_info.get('timerange')}", cfg, provenance_record, ) -def calc_lwt_model( - cfg: dict, cube: iris.cube.Cube, dataset: str, data_info: dict -): +def calc_lwt_model(cfg: dict, cube: iris.cube.Cube, data_info: dict): """Calculate lwt for model data. Args: @@ -1058,22 +1053,20 @@ def calc_lwt_model( data_info : dict Dictionary with info to dataset """ - work_dir = cfg.get("work_dir") - dataset = data_info.get("dataset") - output_file_path = data_info.get("output_file_path") - ensemble = data_info.get("ensemble", "") - timerange = data_info.get("timerange") driver = data_info.get("driver", "") if driver != "": driver = f"_{driver}" - if not os.path.exists(f"{work_dir}/{output_file_path}"): - os.makedirs(f"{work_dir}/{output_file_path}") + if not os.path.exists( + f"{cfg.get('work_dir')}/{data_info.get('output_file_path')}" + ): + os.makedirs( + f"{cfg.get('work_dir')}/{data_info.get('output_file_path')}" + ) - wt = wt_algorithm(cube, dataset) + wt = wt_algorithm(cube, data_info.get("dataset")) - tcoord = cube.coord("time") - time_points = tcoord.units.num2date(tcoord.points) + time_points = cube.coord("time").units.num2date(cube.coord("time").points) wt_cube = iris.cube.CubeList() wt_cube.append(iris.cube.Cube(wt, long_name="lwt")) @@ -1081,37 +1074,37 @@ def calc_lwt_model( logger.info( "Writing Lamb Weathertype for %s \ to file %s.nc", - dataset, - dataset, + data_info.get("dataset"), + data_info.get("dataset"), ) - wt_cube[0].add_dim_coord(tcoord, 0) + wt_cube[0].add_dim_coord(cube.coord("time"), 0) iris.save( wt_cube, - f"{work_dir}/{output_file_path}/{dataset}{driver}" - f"_{ensemble}_{timerange}.nc", + f"{cfg.get('work_dir')}/{data_info.get('output_file_path')}/{data_info.get('dataset')}{driver}" + f"_{data_info.get('ensemble', '')}_{data_info.get('timerange')}.nc", ) # write to csv file d = {"date": time_points[:], "lwt": np.int8(wt)} df = pd.DataFrame(data=d) df.to_csv( - f"{work_dir}/{output_file_path}/{dataset}{driver}_{ensemble}_" - f"{timerange}.csv", + f"{cfg.get('work_dir')}/{data_info.get('output_file_path')}/{data_info.get('dataset')}{driver}_{data_info.get('ensemble', '')}_" + f"{data_info.get('timerange')}.csv", index=False, ) ancestors = [ - f"{work_dir}/wt_mapping_dict_ERA5.json", - f"{work_dir}/wt_mapping_dict_E-OBS.json", + f"{cfg.get('work_dir')}/wt_mapping_dict_ERA5.json", + f"{cfg.get('work_dir')}/wt_mapping_dict_E-OBS.json", ] provenance_record = get_provenance_record( "Lamb Weathertypes", ancestors, ["Lamb Weathertypes"], False, False ) log_provenance( - f"{work_dir}/{output_file_path}/{dataset}_{ensemble}", + f"{cfg.get('work_dir')}/{data_info.get('output_file_path')}/{data_info.get('dataset')}_{data_info.get('ensemble', '')}", cfg, provenance_record, ) diff --git a/esmvaltool/diag_scripts/weathertyping/weathertyping.py b/esmvaltool/diag_scripts/weathertyping/weathertyping.py index 841b8b9b06..b2c9c0ff13 100644 --- a/esmvaltool/diag_scripts/weathertyping/weathertyping.py +++ b/esmvaltool/diag_scripts/weathertyping/weathertyping.py @@ -8,7 +8,7 @@ import iris # import internal esmvaltool modules here -from esmvaltool.diag_scripts.shared import run_diagnostic +from esmvaltool.diag_scripts.shared import group_metadata, run_diagnostic from esmvaltool.diag_scripts.weathertyping.calc_utils import ( calc_lwt_model, calc_lwt_slwt_model, @@ -22,7 +22,6 @@ from esmvaltool.diag_scripts.weathertyping.wt_utils import ( combine_wt_to_file, get_ancestors_era5_eobs, - get_cfg_vars, get_looping_dict, get_model_output_filepath, get_preproc_lists_ensemble, @@ -35,276 +34,346 @@ ) -def run_automatic_slwt(cfg: dict): - """Run the automated calculation for simplified weathertypes \ - and write to file, and plot the means and seasonal occurrence \ - of the weathertypes. +def process_models_automatic_slwt( + cfg: dict, dataset_vars: list, data_info: dict +): + """Process model data for calculating Lamb and simplified weathertypes. Args: ---- cfg : dict Nested dictionary of metadata + dataset_vars : list + List of variable dictionaries for a specific dataset + data_info : dict + Dictionary holding dataset information. """ - preproc_variables_dict, _, _, work_dir, plotting, _, predefined_slwt = ( - get_cfg_vars(cfg) - ) - for dataset_name, dataset_vars in preproc_variables_dict.items(): - data_info = { - "timerange": dataset_vars[0].get("timerange").replace("/", "-"), - "dataset": dataset_name, - } - if dataset_name == "ERA5": - wt_preproc, wt_preproc_prcp, wt_preproc_prcp_eobs = ( - load_wt_preprocessors(dataset_name, preproc_variables_dict) - ) + for ensemble_var in dataset_vars: + if ensemble_var.get("preprocessor") == "weathertype_preproc": + data_info["ensemble"] = ensemble_var.get("ensemble", "") + data_info["driver"] = ensemble_var.get("driver", "") + + if data_info["driver"] != "": + data_info["driver"] = "_" + data_info["driver"] - # calculate lwt - lwt = wt_algorithm(wt_preproc, dataset_name) + wt_preproc = iris.load_cube(ensemble_var.get("filename")) - era5_ancestors, eobs_ancestors = get_ancestors_era5_eobs( - dataset_name, preproc_variables_dict + output_file_path, preproc_path = get_model_output_filepath( + data_info["dataset"], ensemble_var ) - # calculate simplified lwt based on precipitation - # patterns or use predefined_slwt - if not predefined_slwt: - slwt_era5 = calc_slwt_obs( - cfg, - lwt, - wt_preproc_prcp, - dataset_name, - era5_ancestors, - data_info["timerange"], - ) - slwt_eobs = calc_slwt_obs( - cfg, - lwt, - wt_preproc_prcp_eobs, - "E-OBS", - eobs_ancestors, - data_info["timerange"], - ) - else: - slwt_era5, slwt_eobs = run_predefined_slwt( - work_dir, dataset_name, lwt, predefined_slwt - ) + data_info["output_file_path"] = output_file_path + data_info["preproc_path"] = preproc_path - # write to file - wt_list = [lwt, slwt_era5, slwt_eobs] - combine_wt_to_file(cfg, wt_list, wt_preproc, dataset_name) + # calculate weathertypes + calc_lwt_slwt_model( + cfg, wt_preproc, data_info, cfg.get("predefined_slwt") + ) - # load weathertype files as cubes - wt_cubes = load_wt_files(f"{work_dir}/{dataset_name}.nc") + # load wt files + wt_cubes = load_wt_files( + f"{cfg.get('work_dir')}/{output_file_path}" + f"/{data_info['dataset']}" + f"{data_info['driver']}_" + f"{data_info['ensemble']}_" + f"{data_info['timerange']}.nc" + ) - var_dict = get_looping_dict( - dataset_vars - ) # dataset_vars is list of variables for dataset dataset_name - if plotting: - # plot means + var_dict = { + f"{ensemble_var.get('short_name')}": get_preproc_lists_ensemble( + ensemble_var + ) + } + + # plot means + if cfg.get("plotting", False): for var_name, var_data in var_dict.items(): data_info["var"] = var_name data_info["preproc_path"] = var_data[1] plot_means(cfg, var_data[0], wt_cubes, data_info) plot_seasonal_occurrence(cfg, wt_cubes, data_info) - else: - if dataset_name == "E-OBS": - continue - for ensemble_var in dataset_vars: - if ensemble_var.get("preprocessor") == "weathertype_preproc": - data_info["ensemble"] = ensemble_var.get("ensemble", "") - data_info["driver"] = ensemble_var.get("driver", "") - if data_info["driver"] != "": - data_info["driver"] = "_" + data_info["driver"] - wt_preproc = iris.load_cube(ensemble_var.get("filename")) +def process_era5_automatic_slwt( + data_info: dict, + preproc_variables_dict: dict, + cfg: dict, + dataset_vars: list, +): + """Process ERA5 data for calculating Lamb and simplified weathertypes. - output_file_path, preproc_path = get_model_output_filepath( - dataset_name, ensemble_var - ) - - data_info["output_file_path"] = output_file_path - data_info["preproc_path"] = preproc_path + Args: + ----- + data_info : dict + Dictionary holding dataset information. + preproc_variables_dict : dict + Dictionary holding preprocessed variables for all datasets. + cfg : dict + Nested dictionary of metadata + dataset_vars : list + List of variable dictionaries for a specific dataset + """ + wt_preproc, wt_preproc_prcp, wt_preproc_prcp_eobs = load_wt_preprocessors( + data_info["dataset"], preproc_variables_dict + ) - # calculate weathertypes - calc_lwt_slwt_model( - cfg, wt_preproc, data_info, predefined_slwt - ) + # calculate lwt + lwt = wt_algorithm(wt_preproc, data_info["dataset"]) - # load wt files - wt_cubes = load_wt_files( - f"{work_dir}/{output_file_path}" - f"/{dataset_name}" - f"{data_info['driver']}_" - f"{data_info['ensemble']}_" - f"{data_info['timerange']}.nc" - ) + era5_ancestors, eobs_ancestors = get_ancestors_era5_eobs( + data_info["dataset"], preproc_variables_dict + ) - var_dict = { - f"{ensemble_var.get('short_name')}": get_preproc_lists_ensemble( - ensemble_var - ) - } + # calculate simplified lwt based on precipitation + # patterns or use predefined_slwt + if not cfg.get("predefined_slwt"): + slwt_era5 = calc_slwt_obs( + cfg, + lwt, + wt_preproc_prcp, + data_info["dataset"], + era5_ancestors, + data_info["timerange"], + ) + slwt_eobs = calc_slwt_obs( + cfg, + lwt, + wt_preproc_prcp_eobs, + "E-OBS", + eobs_ancestors, + data_info["timerange"], + ) + else: + slwt_era5, slwt_eobs = run_predefined_slwt( + cfg.get("work_dir"), + data_info["dataset"], + lwt, + cfg.get("predefined_slwt"), + ) + + # write to file + wt_list = [lwt, slwt_era5, slwt_eobs] + combine_wt_to_file(cfg, wt_list, wt_preproc, data_info["dataset"]) + + # load weathertype files as cubes + wt_cubes = load_wt_files( + f"{cfg.get('work_dir')}/{data_info['dataset']}.nc" + ) - # plot means - if plotting: - for var_name, var_data in var_dict.items(): - data_info["var"] = var_name - data_info["preproc_path"] = var_data[1] + var_dict = get_looping_dict( + dataset_vars + ) # dataset_vars is list of variables for dataset dataset_name + if cfg.get("plotting", False): + # plot means + for var_name, var_data in var_dict.items(): + data_info["var"] = var_name + data_info["preproc_path"] = var_data[1] - plot_means(cfg, var_data[0], wt_cubes, data_info) - plot_seasonal_occurrence(cfg, wt_cubes, data_info) + plot_means(cfg, var_data[0], wt_cubes, data_info) + plot_seasonal_occurrence(cfg, wt_cubes, data_info) -def run_lwt(cfg: dict): - """Run calculation of weathertypes and write to file, and plot the means \ - and seasonal occurrence of the weathertypes. +def run_automatic_slwt(cfg: dict): + """Run the automated calculation for simplified weathertypes \ + and write to file, and plot the means and seasonal occurrence \ + of the weathertypes. Args: ---- cfg : dict Nested dictionary of metadata - """ - preproc_variables_dict, _, _, work_dir, plotting, _, _ = get_cfg_vars(cfg) + preproc_variables_dict = group_metadata( + cfg.get("input_data").values(), "dataset" + ) for dataset_name, dataset_vars in preproc_variables_dict.items(): data_info = { "timerange": dataset_vars[0].get("timerange").replace("/", "-"), "dataset": dataset_name, } - if dataset_name == "ERA5": - wt_preproc, _, _ = load_wt_preprocessors( - dataset_name, preproc_variables_dict + process_era5_automatic_slwt( + data_info, preproc_variables_dict, cfg, dataset_vars ) + else: + if data_info["dataset"] == "E-OBS": + continue + process_models_automatic_slwt(cfg, dataset_vars, data_info) - # calculate lwt - lwt = wt_algorithm(wt_preproc, dataset_name) - era5_ancestors, eobs_ancestors = get_ancestors_era5_eobs( - dataset_name, preproc_variables_dict - ) +def process_era5_lwt(preproc_variables_dict, cfg, dataset_vars, data_info): + """Process ERA5 data for calculating Lamb weathertypes. + + Args: + ---- + preproc_variables_dict : dict + Dictionary holding preprocessed variables for all datasets. + cfg : dict + Nested dictionary of metadata + dataset_vars : list + List of variable dictionaries for a specific dataset + data_info : dict + Dictionary holding dataset information. + """ + wt_preproc, _, _ = load_wt_preprocessors( + data_info["dataset"], preproc_variables_dict + ) + + # calculate lwt + lwt = wt_algorithm(wt_preproc, data_info["dataset"]) + + era5_ancestors, eobs_ancestors = get_ancestors_era5_eobs( + data_info["dataset"], preproc_variables_dict + ) + + ancestors = [era5_ancestors, eobs_ancestors] + + provenance_record = get_provenance_record( + "Lamb Weathertypes", + ancestors, + ["Lamb Weathertypes"], + False, + False, + ) + + log_provenance(f"{cfg.get('work_dir')}/lwt_era5", cfg, provenance_record) + + # write only lwt to file + write_lwt_to_file(cfg, lwt, wt_preproc, data_info["dataset"]) + + # load wt files + wt_cubes = load_wt_files( + f"{cfg.get('work_dir')}/{data_info['dataset']}.nc", only_lwt=True + ) + + var_dict = get_looping_dict( + dataset_vars + ) # dataset_vars is list of variables for dataset dataset_name + + if cfg.get("plotting", False): + # plot means + for var_name, var_data in var_dict.items(): + data_info["var"] = var_name + data_info["preproc_path"] = var_data[1] - ancestors = [era5_ancestors, eobs_ancestors] + plot_means(cfg, var_data[0], wt_cubes, data_info, only_lwt=True) + plot_seasonal_occurrence(cfg, wt_cubes, data_info) - provenance_record = get_provenance_record( - "Lamb Weathertypes", - ancestors, - ["Lamb Weathertypes"], - False, - False, + +def process_models_lwt(cfg: dict, dataset_vars: list, data_info: dict): + """Process model data for calculating Lamb weathertypes. + + Args: + ---- + cfg : dict + Nested dictionary of metadata + dataset_vars : list + List of variable dictionaries for a specific dataset + data_info : dict + Dictionary holding dataset information. + """ + for ensemble_var in dataset_vars: + if ensemble_var.get("preprocessor") == "weathertype_preproc": + data_info["ensemble"] = ensemble_var.get("ensemble", "") + data_info["driver"] = ensemble_var.get("driver", "") + + if data_info["driver"] != "": + data_info["driver"] = "_" + {data_info["driver"]} + + wt_preproc = iris.load_cube(dataset_vars[0].get("filename")) + + output_file_path, preproc_path = get_model_output_filepath( + data_info["dataset"], dataset_vars ) - log_provenance(f"{work_dir}/lwt_era5", cfg, provenance_record) + data_info["output_file_path"] = output_file_path + data_info["preproc_path"] = preproc_path - # write only lwt to file - write_lwt_to_file(cfg, lwt, wt_preproc, dataset_name) + # calculate weathertypes + calc_lwt_model(cfg, wt_preproc, data_info) # load wt files wt_cubes = load_wt_files( - f"{work_dir}/{dataset_name}.nc", only_lwt=True + f"{cfg.get('work_dir')}/{output_file_path}" + f"/{data_info['dataset']}" + f"{data_info['driver']}" + f"_{data_info['ensemble']}_" + f"{data_info['timerange']}.nc", + only_lwt=True, ) var_dict = get_looping_dict( dataset_vars - ) # dataset_vars is list of variables for dataset dataset_name + ) # dataset_vars is list of variables for dataset_name - if plotting: + if cfg.get("plotting", False): # plot means for var_name, var_data in var_dict.items(): data_info["var"] = var_name data_info["preproc_path"] = var_data[1] plot_means( - cfg, var_data[0], wt_cubes, data_info, only_lwt=True + cfg, + var_data[0], + wt_cubes, + data_info, + only_lwt=True, ) plot_seasonal_occurrence(cfg, wt_cubes, data_info) - else: - if dataset_name == "E-OBS": - continue - for ensemble_var in dataset_vars: - if ensemble_var.get("preprocessor") == "weathertype_preproc": - data_info["ensemble"] = ensemble_var.get("ensemble", "") - data_info["driver"] = ensemble_var.get("driver", "") - if data_info["driver"] != "": - data_info["driver"] = "_" + {data_info["driver"]} - wt_preproc = iris.load_cube( - dataset_vars[0].get("filename") - ) - - output_file_path, preproc_path = get_model_output_filepath( - dataset_name, dataset_vars - ) - - # calculate weathertypes - data_info["output_file_path"] = output_file_path - data_info["preproc_path"] = preproc_path - - calc_lwt_model(cfg, wt_preproc, dataset_name, data_info) - - # load wt files - wt_cubes = load_wt_files( - f"{work_dir}/{output_file_path}" - f"/{dataset_name}" - f"{data_info['driver']}" - f"_{data_info['ensemble']}_" - f"{data_info['timerange']}.nc", - only_lwt=True, - ) +def run_lwt(cfg: dict): + """Run calculation of weathertypes. + Write to file, and plot the means of psl, tas, and pr \ + for each weathertype. \ + Plot seasonal occurrence of the weathertypes. - var_dict = get_looping_dict( - dataset_vars - ) # dataset_vars is list of variables for dataset_name + Args: + ---- + cfg : dict + Nested dictionary of metadata - if plotting: - # plot means - for var_name, var_data in var_dict.items(): - data_info["var"] = var_name - data_info["preproc_path"] = var_data[1] + """ + preproc_variables_dict = group_metadata( + cfg.get("input_data").values(), "dataset" + ) + for dataset_name, dataset_vars in preproc_variables_dict.items(): + data_info = { + "timerange": dataset_vars[0].get("timerange").replace("/", "-"), + "dataset": dataset_name, + } - plot_means( - cfg, - var_data[0], - wt_cubes, - data_info, - only_lwt=True, - ) - plot_seasonal_occurrence(cfg, wt_cubes, data_info) + if dataset_name == "ERA5": + process_era5_lwt( + preproc_variables_dict, cfg, dataset_vars, data_info + ) + else: + if data_info["dataset"] == "E-OBS": + continue + process_models_lwt(cfg, dataset_vars, data_info) def run_my_diagnostic(cfg: dict): """Run the weathertyping diagnostic. - Arguments: + Args: + ----- cfg : dict nested dictionary of metadata - - Returns - string - runs the user diagnostic - """ - # assemble the data dictionary keyed by dataset name - # this makes use of the handy group_metadata function that - # orders the data by 'dataset'; the resulting dictionary is - # keyed on datasets e.g. dict = {'MPI-ESM-LR': [var1, var2...]} - # where var1, var2 are dicts holding all needed information per variable automatic_slwt = cfg.get("automatic_slwt") + # check if user wants to calculate simplified weathertypes automatically if automatic_slwt: run_automatic_slwt(cfg) # if automatic_slwt is false, and predefined_slwt is false, # just write selected pairs to file - elif not automatic_slwt: + else: run_lwt(cfg) if __name__ == "__main__": - # always use run_diagnostic() to get the config (the preprocessor - # nested dictionary holding all the needed information) with run_diagnostic() as config: - # list here the functions that need to run + # main function for running the diagnostic run_my_diagnostic(config) From 65402ba0ad3f2f4c333f3a74d8a26978a1017c1d Mon Sep 17 00:00:00 2001 From: Thomas Kroissenbrunner Date: Tue, 4 Nov 2025 09:22:38 +0100 Subject: [PATCH 07/57] less locals, use function for driver string --- .../diag_scripts/weathertyping/calc_utils.py | 13 ++-- .../diag_scripts/weathertyping/plot_utils.py | 59 +++++++++---------- .../diag_scripts/weathertyping/wt_utils.py | 31 ++++------ 3 files changed, 43 insertions(+), 60 deletions(-) diff --git a/esmvaltool/diag_scripts/weathertyping/calc_utils.py b/esmvaltool/diag_scripts/weathertyping/calc_utils.py index 85c5e3c8e9..fe5f195ae5 100644 --- a/esmvaltool/diag_scripts/weathertyping/calc_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/calc_utils.py @@ -18,6 +18,7 @@ from plot_utils import plot_corr_rmse_heatmaps, plot_maps from wt_utils import ( check_mapping_dict_format, + get_driver, get_mapping_dict, get_provenance_record, log_provenance, @@ -480,9 +481,7 @@ def calc_lwt_slwt_model( If dict, this mapping dict will be used. (see recipe option predefined_slwt) """ - driver = data_info.get("driver", "") - if driver != "": - driver = f"_{driver}" + driver = get_driver(data_info) if not os.path.exists( f"{cfg.get('work_dir')}/{data_info.get('output_file_path')}" @@ -673,9 +672,7 @@ def calc_wt_means( """ var_name = data_info.get("var") wt_string = data_info.get("wt_string") - driver = data_info.get("driver", "") - if driver != "": - driver = f"_{driver}" + driver = get_driver(data_info) logger.info( "Calculating %s %s means for %s", @@ -1053,9 +1050,7 @@ def calc_lwt_model(cfg: dict, cube: iris.cube.Cube, data_info: dict): data_info : dict Dictionary with info to dataset """ - driver = data_info.get("driver", "") - if driver != "": - driver = f"_{driver}" + driver = get_driver(data_info) if not os.path.exists( f"{cfg.get('work_dir')}/{data_info.get('output_file_path')}" diff --git a/esmvaltool/diag_scripts/weathertyping/plot_utils.py b/esmvaltool/diag_scripts/weathertyping/plot_utils.py index 7d30f9332b..7f4c11bfeb 100644 --- a/esmvaltool/diag_scripts/weathertyping/plot_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/plot_utils.py @@ -23,6 +23,7 @@ # local imports from cartopy.mpl.gridliner import LATITUDE_FORMATTER, LONGITUDE_FORMATTER from matplotlib.colors import ListedColormap +from wt_utils import get_driver iris.FUTURE.datum_support = True @@ -68,10 +69,7 @@ def plot_seasonal_occurrence( data_info : dict Dictionary with relevant info to dataset """ - dataset_name = data_info.get("dataset") - timerange = data_info.get("timerange") - ensemble = data_info.get("ensemble", "") - driver = data_info.get("driver", "") + driver = get_driver(data_info) output_path = f"{cfg['plot_dir']}/seasonal_occurrence" @@ -130,7 +128,7 @@ def plot_seasonal_occurrence( ax_.set_title( f"Seasonal occurence of {wt_string}, \ - {dataset_name}, {timerange}" + {data_info.get('dataset')}, {data_info.get('timerange')}" ) ax_.stackplot(x, y, colors=colors) @@ -154,12 +152,12 @@ def plot_seasonal_occurrence( ax_.set_ylabel("Cumulative Relative occurrence") plt.savefig( - f"{output_path}/{driver}{dataset_name}_{ensemble}_" - f"{wt_string}_rel_occurrence_{timerange}.png" + f"{output_path}/{driver}{data_info.get('dataset')}_{data_info.get('ensemble', '')}_" + f"{wt_string}_rel_occurrence_{data_info.get('timerange')}.png" ) plt.savefig( - f"{output_path}/{driver}{dataset_name}_{ensemble}_" - f"{wt_string}_rel_occurrence_{timerange}.pdf" + f"{output_path}/{driver}{data_info.get('dataset')}_{data_info.get('ensemble', '')}_" + f"{wt_string}_rel_occurrence_{data_info.get('timerange')}.pdf" ) plt.close() @@ -182,17 +180,16 @@ def plot_maps( mode : str Statistics that is used """ - dataset = data_info.get("dataset") var_name = data_info.get("var") - wt_string = data_info.get("wt_string") - ensemble = data_info.get("ensemble", "") - timerange = data_info.get("timerange") - driver = data_info.get("driver", "") - if driver != "": - driver = f"_{driver}" + driver = get_driver(data_info) logger.info( - "Plotting %s %s %s for %s %s", dataset, var_name, mode, wt_string, wt + "Plotting %s %s %s for %s %s", + data_info.get("dataset"), + data_info.get("var"), + mode, + data_info.get("wt_string"), + wt, ) local_path = f"{cfg.get('plot_dir')}/{mode}" @@ -202,11 +199,11 @@ def plot_maps( ax = plt.axes(projection=ccrs.PlateCarree()) - if var_name == "psl": + if data_info.get("var") == "psl": psl_cmap = get_colormap("psl") plt.title( - f"{dataset} {ensemble}, {var_name} {mode}\n" - + f"{timerange}, wt: {wt}" + f"{data_info.get('dataset')} {data_info.get('ensemble', '')}, {data_info.get('var')} {mode}\n" + + f"{data_info.get('timerange')}, wt: {wt}" ) unit = "[hPa]" im = iplt.contourf(cube / 100, cmap=psl_cmap) @@ -215,17 +212,17 @@ def plot_maps( cb.set_label(label=f"{var_name} {mode} {unit}") elif var_name == "pr": prcp_cmap = get_colormap("prcp") - if dataset == "ERA5": + if data_info.get("dataset") == "ERA5": unit = "[m]" plt.title( - f"{dataset} {ensemble}, total {var_name} {mode}\n" - + f"{timerange}, wt: {wt}" + f"{data_info.get('dataset')} {data_info.get('ensemble', '')}, total {var_name} {mode}\n" + + f"{data_info.get('timerange')}, wt: {wt}" ) else: unit = "[kg m-2 s-1]" plt.title( - f"{dataset} {ensemble}, {var_name} flux {mode}\n" - + f"{timerange}, wt: {wt}" + f"{data_info.get('dataset')} {data_info.get('ensemble', '')}, {var_name} flux {mode}\n" + + f"{data_info.get('timerange')}, wt: {wt}" ) im = iplt.contourf(cube, cmap=prcp_cmap) cb = plt.colorbar(im) @@ -235,8 +232,8 @@ def plot_maps( temp_cmap = get_colormap("temp") unit = "[K]" plt.title( - f"{dataset} {ensemble}, 1000 hPa {var_name} {mode}\n" - + f"{timerange}, wt: {wt}" + f"{data_info.get('dataset')} {data_info.get('ensemble', '')}, 1000 hPa {var_name} {mode}\n" + + f"{data_info.get('timerange')}, wt: {wt}" ) im = iplt.contourf(cube, cmap=temp_cmap) cb = plt.colorbar(im) @@ -269,12 +266,12 @@ def plot_maps( ax.add_feature(cfeature.BORDERS, linestyle=":") plt.savefig( - f"{local_path}/{wt_string}_{wt}{driver}_{dataset}_{ensemble}" - f"_{var_name}_{mode}_{timerange}.png" + f"{local_path}/{data_info.get('wt_string')}_{wt}{driver}_{data_info.get('dataset')}_{data_info.get('ensemble', '')}" + f"_{var_name}_{mode}_{data_info.get('timerange')}.png" ) plt.savefig( - f"{local_path}/{wt_string}_{wt}{driver}_{dataset}_{ensemble}_" - f"{var_name}_{mode}_{timerange}.pdf" + f"{local_path}/{data_info.get('wt_string')}_{wt}{driver}_{data_info.get('dataset')}_{data_info.get('ensemble', '')}_" + f"{var_name}_{mode}_{data_info.get('timerange')}.pdf" ) plt.close() diff --git a/esmvaltool/diag_scripts/weathertyping/wt_utils.py b/esmvaltool/diag_scripts/weathertyping/wt_utils.py index 4f905dff88..ab57ab3115 100644 --- a/esmvaltool/diag_scripts/weathertyping/wt_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/wt_utils.py @@ -15,7 +15,7 @@ import pandas as pd # import internal esmvaltool modules here -from esmvaltool.diag_scripts.shared import ProvenanceLogger, group_metadata +from esmvaltool.diag_scripts.shared import ProvenanceLogger iris.FUTURE.datum_support = True @@ -25,32 +25,23 @@ warnings.filterwarnings("ignore", ".*Collapsing a non-contiguous coordinate*") -def get_cfg_vars(cfg: dict): - """Get list of vars from configuration dict. +def get_driver(data_info: dict) -> str: + """Get driving model string. Args: ---- - cfg : dict - Configuration dict from recipe. + data_info : dict + Data information dictionary. Returns ------- - tuple - cfg vars + str + Driver string with leading underscore or empty string. """ - preproc_variables_dict = group_metadata( - cfg.get("input_data").values(), "dataset" - ) - - return ( - preproc_variables_dict, - cfg.get("correlation_threshold"), - cfg.get("rmse_threshold"), - cfg.get("work_dir"), - cfg.get("plotting", False), - cfg.get("automatic_slwt", True), - cfg.get("predefined_slwt", False), - ) + driver = data_info.get("driver", "") + if driver != "": + driver = f"_{driver}" + return driver def load_wt_preprocessors(dataset: str, preproc_variables_dict: dict): From 57e5deaa51251ddb179b2d90c5e7d3b7084769bb Mon Sep 17 00:00:00 2001 From: Thomas Kroissenbrunner Date: Tue, 4 Nov 2025 10:00:19 +0100 Subject: [PATCH 08/57] refactor calc_wt_means, calc_wt_std, calc_wt_anomaly --- .../diag_scripts/weathertyping/calc_utils.py | 265 +++++------------- 1 file changed, 73 insertions(+), 192 deletions(-) diff --git a/esmvaltool/diag_scripts/weathertyping/calc_utils.py b/esmvaltool/diag_scripts/weathertyping/calc_utils.py index fe5f195ae5..019660b1fb 100644 --- a/esmvaltool/diag_scripts/weathertyping/calc_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/calc_utils.py @@ -681,71 +681,40 @@ def calc_wt_means( wt_string, ) - num_slwt = 0 - target_indices = [] - lwt, slwt_eobs, slwt_era5 = [], [], [] - if wt_string == "slwt_ERA5": slwt_era5_cube = wt_cubes[1] tcoord = slwt_era5_cube.coord("time") - slwt_era5 = slwt_era5_cube.data[:] - num_slwt = len(np.unique(slwt_era5)) + wt_array = slwt_era5_cube.data[:] elif wt_string == "slwt_EOBS": slwt_eobs_cube = wt_cubes[2] tcoord = slwt_eobs_cube.coord("time") - slwt_eobs = slwt_eobs_cube.data[:] - num_slwt = len(np.unique(slwt_eobs)) + wt_array = slwt_eobs_cube.data[:] elif wt_string == "lwt": lwt_cube = wt_cubes[0] tcoord = lwt_cube.coord("time") - lwt = lwt_cube.data[:] - - if "slwt" in wt_string: - for wt in range(1, num_slwt + 1): - if wt_string == "slwt_ERA5": - target_indices = np.where(slwt_era5 == wt) - elif wt_string == "slwt_EOBS": - target_indices = np.where(slwt_eobs == wt) - else: - logger.info("WT_STRING not supported!") - if len(target_indices[0]) < 1: - logger.info( - "calc_wt_means - CAUTION: Skipped %s %s \ - for dataset %s!", - wt_string, - wt, - data_info.get("dataset"), - ) - continue - dates = [ - tcoord.units.num2date(tcoord.points[i]) for i in target_indices - ] - extracted_cube = cube.extract( - iris.Constraint(time=lambda t, d=dates: t.point in d[0]) - ) - wt_cube_mean = extracted_cube.collapsed("time", iris.analysis.MEAN) - plot_maps(wt, cfg, wt_cube_mean, data_info, "mean") - elif wt_string == "lwt": - for wt in range(1, 28): - target_indices = np.where(lwt == wt) - if len(target_indices[0]) < 1: - logger.info( - "calc_wt_means - CAUTION: Skipped lwt %s \ - for dataset %s!", - wt, - data_info.get("dataset"), - ) - continue - dates = [ - tcoord.units.num2date(tcoord.points[i]) for i in target_indices - ] - extracted_cube = cube.extract( - iris.Constraint(time=lambda t, d=dates: t.point in d[0]) - ) - wt_cube_mean = extracted_cube.collapsed("time", iris.analysis.MEAN) - plot_maps(wt, cfg, wt_cube_mean, data_info, "mean") + wt_array = lwt_cube.data[:] else: - logger.info("WT_STRING NOT SUPPORTED.") + raise NameError("wt_array does not exist in line 699.") + + for wt in range(1, len(np.unique(wt_array)) + 1): + target_indices = np.where(wt_array == wt) + if len(target_indices[0]) < 1: + logger.info( + "calc_wt_means - CAUTION: Skipped %s %s \ + for dataset %s!", + wt_string, + wt, + data_info.get("dataset"), + ) + continue + dates = [ + tcoord.units.num2date(tcoord.points[i]) for i in target_indices + ] + extracted_cube = cube.extract( + iris.Constraint(time=lambda t, d=dates: t.point in d[0]) + ) + wt_cube_mean = extracted_cube.collapsed("time", iris.analysis.MEAN) + plot_maps(wt, cfg, wt_cube_mean, data_info, "mean") ancestors = [ f"{data_info.get('preproc_path')}", @@ -799,90 +768,45 @@ def calc_wt_anomalies( wt_string, ) - target_indices = [] - lwt, slwt_eobs, slwt_era5 = [], [], [] - if wt_string == "slwt_ERA5": slwt_era5_cube = wt_cubes[1] tcoord = slwt_era5_cube.coord("time") - slwt_era5 = slwt_era5_cube.data[:] + wt_array = slwt_era5_cube.data[:] elif wt_string == "slwt_EOBS": slwt_eobs_cube = wt_cubes[2] tcoord = slwt_eobs_cube.coord("time") - slwt_eobs = slwt_eobs_cube.data[:] + wt_array = slwt_eobs_cube.data[:] elif wt_string == "lwt": lwt_cube = wt_cubes[0] tcoord = lwt_cube.coord("time") - lwt = lwt_cube.data[:] - - num_slwt_era5 = len(np.unique(slwt_era5)) - num_slwt_eobs = len(np.unique(slwt_eobs)) - - if num_slwt_eobs != num_slwt_era5: - logger.info( - "calc_wt_anomalies - CAUTION: unequal number of \ - slwt_era5 (%s) and slwt_eobs (%s)!", - num_slwt_era5, - num_slwt_eobs, - ) + wt_array = lwt_cube.data[:] + else: + raise NameError("wt_array does not exist in line 817.") - if "slwt" in wt_string: - for wt in range(1, max(num_slwt_era5, num_slwt_eobs)): - if wt_string == "slwt_ERA5": - target_indices = np.where(slwt_era5 == wt) - elif wt_string == "slwt_EOBS": - target_indices = np.where(slwt_eobs == wt) - else: - logger.info("WT_STRING not supported!") - if len(target_indices[0]) < 1: - logger.info( - "calc_wt_anomalies - CAUTION: Skipped wt %s \ - for dataset %s!", - wt, - data_info.get("dataset"), - ) - continue - dates = [ - tcoord.units.num2date(tcoord.points[i]) for i in target_indices - ] - extracted_cube = cube.extract( - iris.Constraint(time=lambda t, d=dates: t.point in d[0]) - ) - wt_cube_mean = extracted_cube.collapsed("time", iris.analysis.MEAN) - plot_maps( - wt, - cfg, - cube.collapsed("time", iris.analysis.MEAN) - wt_cube_mean, - data_info, - "anomaly", - ) - elif wt_string == "lwt": - for wt in range(1, 28): - target_indices = np.where(lwt == wt) - if len(target_indices[0]) < 1: - logger.info( - "calc_wt_anomalies - CAUTION: Skipped wt %s \ - for dataset %s!", - wt, - data_info.get("dataset"), - ) - continue - dates = [ - tcoord.units.num2date(tcoord.points[i]) for i in target_indices - ] - extracted_cube = cube.extract( - iris.Constraint(time=lambda t, d=dates: t.point in d[0]) - ) - wt_cube_mean = extracted_cube.collapsed("time", iris.analysis.MEAN) - plot_maps( + for wt in range(1, len(np.unique(wt_array)) + 1): + target_indices = np.where(wt_array == wt) + if len(target_indices[0]) < 1: + logger.info( + "calc_wt_anomalies - CAUTION: Skipped wt %s \ + for dataset %s!", wt, - cfg, - cube.collapsed("time", iris.analysis.MEAN) - wt_cube_mean, - data_info, - "anomaly", + data_info.get("dataset"), ) - else: - logger.info("WT_STRING NOT SUPPORTED.") + continue + dates = [ + tcoord.units.num2date(tcoord.points[i]) for i in target_indices + ] + extracted_cube = cube.extract( + iris.Constraint(time=lambda t, d=dates: t.point in d[0]) + ) + wt_cube_mean = extracted_cube.collapsed("time", iris.analysis.MEAN) + plot_maps( + wt, + cfg, + cube.collapsed("time", iris.analysis.MEAN) - wt_cube_mean, + data_info, + "anomaly", + ) ancestors = [ f"{data_info.get('preproc_path')}", @@ -935,82 +859,39 @@ def calc_wt_std( wt_string, ) - target_indices = [] - lwt, slwt_eobs, slwt_era5 = [], [], [] - if wt_string == "slwt_ERA5": slwt_era5_cube = wt_cubes[1] tcoord = slwt_era5_cube.coord("time") - slwt_era5 = slwt_era5_cube.data[:] + wt_array = slwt_era5_cube.data[:] elif wt_string == "slwt_EOBS": slwt_eobs_cube = wt_cubes[2] tcoord = slwt_eobs_cube.coord("time") - slwt_eobs = slwt_eobs_cube.data[:] + wt_array = slwt_eobs_cube.data[:] elif wt_string == "lwt": lwt_cube = wt_cubes[0] tcoord = lwt_cube.coord("time") - lwt = lwt_cube.data[:] - - num_slwt_era5 = len(np.unique(slwt_era5)) - num_slwt_eobs = len(np.unique(slwt_eobs)) - - if num_slwt_eobs != num_slwt_era5: - logger.info( - "calc_wt_std - CAUTION: unequal number of \ - slwt_era5 (%s) and slwt_eobs (%s)!", - num_slwt_era5, - num_slwt_eobs, - ) + wt_array = lwt_cube.data[:] + else: + raise NameError("wt_array does not exist in line 353.") - if "slwt" in wt_string: - for wt in range(1, max(num_slwt_era5, num_slwt_eobs)): - if wt_string == "slwt_ERA5": - target_indices = np.where(slwt_era5 == wt) - elif wt_string == "slwt_EOBS": - target_indices = np.where(slwt_eobs == wt) - else: - logger.info("WT_STRING not supported!") - if len(target_indices[0]) < 1: - logger.info( - "calc_slwt_obs - CAUTION: Skipped wt %s \ - for dataset %s!", - wt, - data_info.get("dataset"), - ) - continue - dates = [ - tcoord.units.num2date(tcoord.points[i]) for i in target_indices - ] - extracted_cube = cube.extract( - iris.Constraint(time=lambda t, d=dates: t.point in d[0]) - ) - wt_cube_std = extracted_cube.collapsed( - "time", iris.analysis.STD_DEV - ) - plot_maps(wt, cfg, wt_cube_std, data_info, "stddev") - elif wt_string == "lwt": - for wt in range(1, 28): - target_indices = np.where(lwt == wt) - if len(target_indices[0]) < 1: - logger.info( - "calc_wt_std - CAUTION: Skipped wt %s \ - for dataset %s!", - wt, - data_info.get("dataset"), - ) - continue - dates = [ - tcoord.units.num2date(tcoord.points[i]) for i in target_indices - ] - extracted_cube = cube.extract( - iris.Constraint(time=lambda t, d=dates: t.point in d[0]) - ) - wt_cube_std = extracted_cube.collapsed( - "time", iris.analysis.STD_DEV + for wt in range(1, len(np.unique(wt_array)) + 1): + target_indices = np.where(wt_array == wt) + if len(target_indices[0]) < 1: + logger.info( + "calc_slwt_obs - CAUTION: Skipped wt %s \ + for dataset %s!", + wt, + data_info.get("dataset"), ) - plot_maps(wt, cfg, wt_cube_std, data_info, "stddev") - else: - logger.info("WT_STRING NOT SUPPORTED.") + continue + dates = [ + tcoord.units.num2date(tcoord.points[i]) for i in target_indices + ] + extracted_cube = cube.extract( + iris.Constraint(time=lambda t, d=dates: t.point in d[0]) + ) + wt_cube_std = extracted_cube.collapsed("time", iris.analysis.STD_DEV) + plot_maps(wt, cfg, wt_cube_std, data_info, "stddev") ancestors = [ f"{data_info.get('preproc_path')}", From 174126effb56d96fd1f267bb999aa753b4103b51 Mon Sep 17 00:00:00 2001 From: Thomas Kroissenbrunner Date: Tue, 4 Nov 2025 11:04:40 +0100 Subject: [PATCH 09/57] docstrings --- .../diag_scripts/weathertyping/calc_utils.py | 330 ++++++------------ .../diag_scripts/weathertyping/plot_utils.py | 111 +++--- .../weathertyping/weathertyping.py | 59 +--- .../diag_scripts/weathertyping/wt_utils.py | 330 ++++++------------ 4 files changed, 281 insertions(+), 549 deletions(-) diff --git a/esmvaltool/diag_scripts/weathertyping/calc_utils.py b/esmvaltool/diag_scripts/weathertyping/calc_utils.py index 019660b1fb..769771ee8f 100644 --- a/esmvaltool/diag_scripts/weathertyping/calc_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/calc_utils.py @@ -44,28 +44,18 @@ def calc_slwt_obs( ancestors: list, timerange: str, ) -> np.array: - """Calculate simplified weathertypes for observation datasets based on \ - precipitation patterns over specified area. + """Calculate simplified weathertypes for observational data. Args: - ---- - cfg : dict - Configuration dictionary from recipe - lwt : np.array - Array of Lamb WT - cube : iris.cube.Cube - Cube of psl data - dataset : str - Name of dataset - ancestors : list - List of ancestors - timerange : str - Time range for the calculation - - Returns - ------- - np.array - Simplified Lamb Weathertypes + cfg (dict): Configuration dictionary from recipe + lwt (np.array): Array of Lamb WT + cube (iris.cube.Cube): Cube of psl data + dataset (str): Name of dataset + ancestors (list): List of ancestors + timerange (str): Time range for the calculation + + Returns: + np.array: Simplified Lamb Weathertypes """ logger.info("Calculating simplified Lamb Weathertypes for %s", dataset) @@ -131,10 +121,8 @@ def calc_const(): scheme. Int. J. Climatol., 13: 655-663. https://doi.org/10.1002/joc.3370130606 - Returns - ------- - tuple - The four constants needed for WT calculation. + Returns: + tuple: The four constants needed for WT calculation. """ const1 = 1 / np.cos(45 * np.pi / 180) const2 = np.sin(45 * np.pi / 180) / np.sin(40 * np.pi / 180) @@ -153,14 +141,10 @@ def calc_westerly_flow(cube: iris.cube.Cube) -> np.array: Int. J. Climatol., 13: 655-663. https://doi.org/10.1002/joc.3370130606 Args: - ---- - cube : iris.cube.Cube - Cube of psl data. + cube (iris.cube.Cube): Cube of psl data. - Returns - ------- - np.array - westerly flow + Returns: + np.array: westerly flow """ return 1 / 2 * (cube.data[:, 1, 2] + cube.data[:, 1, 4]) - 1 / 2 * ( cube.data[:, 3, 2] + cube.data[:, 3, 4] @@ -176,15 +160,11 @@ def calc_southerly_flow(cube: iris.cube.Cube, const1: float) -> np.array: Int. J. Climatol., 13: 655-663. https://doi.org/10.1002/joc.3370130606 Args: - ---- - cube : iris.cube.Cube - cube of psl data - const1 : float - const1 - Returns - ------- - np.array - southerly flow + cube (iris.cube.Cube): cube of psl data + const1 (float): const1 + + Returns: + np.array: southerly flow """ return const1 * ( 1 @@ -208,15 +188,12 @@ def calc_resultant_flow( Args: ---- - westerly_flow : np.array - westerly flow - southerly_flow : np.array - southerly flow + westerly_flow (np.array): westerly flow + southerly_flow (np.array): southerly flow Returns ------- - np.array - resultant flow + np.array: resultant flow """ return (southerly_flow**2 + westerly_flow**2) ** (1 / 2) @@ -233,17 +210,13 @@ def calc_westerly_shear_velocity( Args: ---- - cube : iris.cube.Cube - cube of psl data - const2 : float - const2 - const3 : float - const3 + cube (iris.cube.Cube): cube of psl data + const2 (float): const2 + const3 (float): const3 Returns ------- - np.array - westerly shear velocity + np.array: westerly shear velocity """ return const2 * ( 1 / 2 * (cube.data[:, 0, 2] + cube.data[:, 0, 4]) @@ -266,15 +239,12 @@ def calc_southerly_shear_velocity( Args: ---- - cube : iris.cube.Cube - cube of psl data - const4 : float - const + cube (iris.cube.Cube): cube of psl data + const4 (float): const Returns ------- - np.array - southerly shear velocity + np.array: southerly shear velocity """ return const4 * ( 1 @@ -303,16 +273,11 @@ def calc_total_shear_velocity( Int. J. Climatol., 13: 655-663. https://doi.org/10.1002/joc.3370130606 Args: - ---- - westerly_shear_velocity : np.array - westerly shear velocity - southerly_shear_velocity : np.array - southerly shear velocity + westerly_shear_velocity (np.array): westerly shear velocity + southerly_shear_velocity (np.array): southerly shear velocity Returns - ------- - np.array - total shear velocity + np.array:total shear velocity """ return westerly_shear_velocity + southerly_shear_velocity @@ -326,17 +291,13 @@ def wt_algorithm(cube: iris.cube.Cube, dataset: str) -> np.array: Int. J. Climatol., 13: 655-663. https://doi.org/10.1002/joc.3370130606 Args: - ---- - cube : iris.cube.Cube - Cube of psl data. - dataset : str - Name of dataset. + cube (iris.cube.Cube): Cube of psl data + dataset (str): Name of dataset - Returns - ------- - np.array - Array of Lamb WT for each day + Returns: + np.array: Lamb weathertypes """ + # lats and lons corresponding to datapoints # 55, 5 -> 1 # 55, 15 -> 2 @@ -466,18 +427,13 @@ def calc_lwt_slwt_model( data_info: dict, predefined_slwt: bool | dict, ): - """Calculate Lamb WT and simplified WT for model data. + """Calculate Lamb as well as simplified weathertypes for model. Args: - ---- - cfg : dict - Configuration dictionary from recipe - cube : iris.cube.Cube - PSL field of dataset - data_info : dict - Dictionary with info to dataset - predefined_slwt : bool | dict - If False, automatic_slwt will be used. + cfg (dict): Configuration dictionary from recipe + cube (iris.cube.Cube): PSL field of dataset + data_info (dict): Dictionary with info to dataset + predefined_slwt (bool | dict): If False, automatic_slwt will be used. If dict, this mapping dict will be used. (see recipe option predefined_slwt) """ @@ -570,19 +526,7 @@ def calc_lwt_slwt_model( def rmse(subarray1: np.array, subarray2: np.array) -> np.array: - """Calculate rsme. - - Args: - subarray1 : np.array - First subarray - subarray2 : np.array - Second subarray - - Returns - ------- - np.array - rsme array - """ + """Calculate root mean square error between two arrays.""" return np.sqrt(np.mean((subarray1 - subarray2) ** 2)) @@ -592,31 +536,16 @@ def process_prcp_mean( dataset: str, timerange: str, ) -> list: - """Process precipitation fields. - - Specified area to get a list of selected pairs of weathertypes - with the highest correlation (higher than correlation_threshold) - and smallest RSME (smaller than rsme_threshold) for - further processing and simplifying the WT. + """Return which weathertypes can be grouped together for a certain precipitation pattern. Args: - cfg : dict - Configuration dictionary from recipe - data : np.array - Array of precipitation means for each WT - correlation_threshold : float - correlation threshold - rmse_threshold : float - rsme threshold - dataset : str - Name of dataset - timerange : str - Time range of dataset + cfg (dict): Configuration dictionary from recipe + data (np.array): Array of precipitation means for each WT + dataset (str): Name of dataset + timerange (str): Time range of dataset - Returns - ------- - list - Selected pairs of WT. This is passed to get_mapping_dict + Returns: + list: Selected pairs of WT. This is passed to get_mapping_dict """ logger.info("Calculating corr and rsme matrices for %s", dataset) @@ -658,17 +587,13 @@ def calc_wt_means( wt_cubes: iris.cube.CubeList, data_info: dict, ): - """Calculate means of fields of each weathertype. + """Calculate means for psl, tas or pr for weathertypes. Args: - cfg : dict - Configuration dictionary from recipe - cube : iris.cube.Cube - Cube with variable data - wt_cubes : iris.cube.CubeList - List of cubes of lwt, slwt_ERA5 and slwt_EOBS - data_info : dict - Dictionary with info to dataset + cfg (dict): Configuration dictionary from recipe + cube (iris.cube.Cube): Cube with variable data + wt_cubes (iris.cube.CubeList): List of cubes of lwt, slwt_ERA5 and slwt_EOBS + data_info (dict): Dictionary with info to dataset """ var_name = data_info.get("var") wt_string = data_info.get("wt_string") @@ -681,20 +606,7 @@ def calc_wt_means( wt_string, ) - if wt_string == "slwt_ERA5": - slwt_era5_cube = wt_cubes[1] - tcoord = slwt_era5_cube.coord("time") - wt_array = slwt_era5_cube.data[:] - elif wt_string == "slwt_EOBS": - slwt_eobs_cube = wt_cubes[2] - tcoord = slwt_eobs_cube.coord("time") - wt_array = slwt_eobs_cube.data[:] - elif wt_string == "lwt": - lwt_cube = wt_cubes[0] - tcoord = lwt_cube.coord("time") - wt_array = lwt_cube.data[:] - else: - raise NameError("wt_array does not exist in line 699.") + wt_array, tcoord = get_wt_array(wt_string, wt_cubes) for wt in range(1, len(np.unique(wt_array)) + 1): target_indices = np.where(wt_array == wt) @@ -740,23 +652,52 @@ def calc_wt_means( ) +def get_wt_array(wt_string: str, wt_cubes: iris.cube.CubeList) -> np.array: + """Get weathertype array and time coordinate based on wt_string. + + Args: + wt_string(str): string for weathertype selection + wt_cubes(iris.cube.CubeList): list of weathertype cubes + + Raises: + NameError: if wt_array does not exist for the given wt_string + + Returns: + (np.array, np.array): weathertype array and time coordinate + """ + if wt_string == "slwt_ERA5": + slwt_era5_cube = wt_cubes[1] + tcoord = slwt_era5_cube.coord("time") + wt_array = slwt_era5_cube.data[:] + elif wt_string == "slwt_EOBS": + slwt_eobs_cube = wt_cubes[2] + tcoord = slwt_eobs_cube.coord("time") + wt_array = slwt_eobs_cube.data[:] + elif wt_string == "lwt": + lwt_cube = wt_cubes[0] + tcoord = lwt_cube.coord("time") + wt_array = lwt_cube.data[:] + else: + raise NameError( + "wt_array does not exist for calc_wt_means, calc_wt_anomalies or calc_wt_std." + ) + + return wt_array, tcoord + + def calc_wt_anomalies( cfg: dict, cube: iris.cube.Cube, wt_cubes: iris.cube.CubeList, data_info: dict, ): - """Calculate anomalies of fields of each weathertype. + """Calculate anomalies for psl, tas and pr for weathertypes. Args: - cfg : dict - Configuration dictionary from recipe - cube : iris.cube.Cube - Cube with variable data - wt_cubes : iris.cube.CubeList - List of cubes of lwt, slwt_ERA5 and slwt_EOBS - data_info : dict - Dictionary with info to dataset + cfg (dict): Configuration dictionary from recipe + cube (iris.cube.Cube): Cube with variable data + wt_cubes (iris.cube.CubeList): List of cubes of lwt, slwt_ERA5 and slwt_EOBS + data_info (dict): Dictionary with info to dataset """ var_name = data_info.get("var_name") wt_string = data_info.get("wt_string") @@ -768,20 +709,7 @@ def calc_wt_anomalies( wt_string, ) - if wt_string == "slwt_ERA5": - slwt_era5_cube = wt_cubes[1] - tcoord = slwt_era5_cube.coord("time") - wt_array = slwt_era5_cube.data[:] - elif wt_string == "slwt_EOBS": - slwt_eobs_cube = wt_cubes[2] - tcoord = slwt_eobs_cube.coord("time") - wt_array = slwt_eobs_cube.data[:] - elif wt_string == "lwt": - lwt_cube = wt_cubes[0] - tcoord = lwt_cube.coord("time") - wt_array = lwt_cube.data[:] - else: - raise NameError("wt_array does not exist in line 817.") + wt_array, tcoord = get_wt_array(wt_string, wt_cubes) for wt in range(1, len(np.unique(wt_array)) + 1): target_indices = np.where(wt_array == wt) @@ -821,10 +749,8 @@ def calc_wt_anomalies( ["anomaly"], ) - local_path = f"{cfg.get('plot_dir')}/anomaly" - log_provenance( - f"{local_path}/{wt_string}_{wt}_{data_info.get('dataset')}_{data_info.get('ensemble', '')}" + f"{cfg.get('plot_dir')}/anomaly/{wt_string}_{wt}_{data_info.get('dataset')}_{data_info.get('ensemble', '')}" f"_{var_name}_anomaly__{data_info.get('timerange')}", cfg, provenance_record, @@ -837,17 +763,13 @@ def calc_wt_std( wt_cubes: iris.cube.CubeList, data_info: dict, ): - """Calculate standard deviation of fields of each weathertype. + """Calculate standard deviation for psl, tas and pr for weathertypes. Args: - cfg : dict - Configuration dictionary from recipe - cube : iris.cube.Cube - Cube with variable data - wt_cubes : iris.cube.CubeList - List of cubes of lwt, slwt_ERA5 and slwt_EOBS - data_info : dict - Dictionary with info to dataset + cfg (dict): Configuration dictionary from recipe + cube (iris.cube.Cube): Cube with variable data + wt_cubes (iris.cube.CubeList): List of cubes of lwt, slwt_ERA5 and slwt_EOBS + data_info (dict): Dictionary with info to dataset """ var_name = data_info.get("var_name") wt_string = data_info.get("wt_string") @@ -859,20 +781,7 @@ def calc_wt_std( wt_string, ) - if wt_string == "slwt_ERA5": - slwt_era5_cube = wt_cubes[1] - tcoord = slwt_era5_cube.coord("time") - wt_array = slwt_era5_cube.data[:] - elif wt_string == "slwt_EOBS": - slwt_eobs_cube = wt_cubes[2] - tcoord = slwt_eobs_cube.coord("time") - wt_array = slwt_eobs_cube.data[:] - elif wt_string == "lwt": - lwt_cube = wt_cubes[0] - tcoord = lwt_cube.coord("time") - wt_array = lwt_cube.data[:] - else: - raise NameError("wt_array does not exist in line 353.") + wt_array, tcoord = get_wt_array(wt_string, wt_cubes) for wt in range(1, len(np.unique(wt_array)) + 1): target_indices = np.where(wt_array == wt) @@ -918,18 +827,12 @@ def calc_wt_std( def calc_lwt_model(cfg: dict, cube: iris.cube.Cube, data_info: dict): - """Calculate lwt for model data. + """Calculate Lamb weathertypes for models. Args: - ---- - cfg : dict - Configuration dictionary from recipe - cube : iris.cube.Cube - Cube to keep time coordinate - dataset : str - Name of dataset - data_info : dict - Dictionary with info to dataset + cfg (dict): Configuration dictionary from recipe + cube (iris.cube.Cube): Cube with variable data + data_info (dict): Dictionary with info to dataset """ driver = get_driver(data_info) @@ -993,20 +896,15 @@ def plot_means( data_info: dict, only_lwt=False, ): - """Wrapper function to plot various means/std/anomalies. + """Wrapper function for plotting means, anomalies and standard deviations. Args: - cfg : dict - Configuration dictionary from recipe - preproc_var : np.array - Preprocessed variable cube - wt_cubes : iris.cube.Cube - List of cubes of lwt, slwt_ERA5 and slwt_EOBS - data_info : dict - Dictionary with info to dataset - only_lwt : bool - If True, only lwt means are calculated. - If False, lwt, slwt_ERA5 and slwt_EOBS means are calculated. + cfg (dict): Configuration dictionary from recipe + preproc_var (np.array): Preprocessed variable cube + wt_cubes (iris.cube.Cube): List of cubes of lwt, slwt_ERA5 and slwt_EOBS + data_info (dict): Dictionary with info to dataset + only_lwt (bool, optional): If True, only lwt means are calculated. + If False, lwt, slwt_ERA5 and slwt_EOBS means are calculated. Defaults to False. """ if not only_lwt: data_info["wt_string"] = "lwt" diff --git a/esmvaltool/diag_scripts/weathertyping/plot_utils.py b/esmvaltool/diag_scripts/weathertyping/plot_utils.py index 7f4c11bfeb..56a496e6a0 100644 --- a/esmvaltool/diag_scripts/weathertyping/plot_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/plot_utils.py @@ -34,17 +34,13 @@ def generate_grayscale_hex_values(x): - """Generate grayscale values for plotting seasonal occurrence. + """Generate grayscale values for plotting seasonal occurences. Args: - ---- - x : int - number of weathertypes - - Returns - ------- - np.list - array with grayscale values as hex + x (list): Amount of weathertypes. + + Returns: + list: List of grayscale hex values """ grayscale_values = np.linspace(0, 1, x) grayscale_hex = [ @@ -58,16 +54,12 @@ def generate_grayscale_hex_values(x): def plot_seasonal_occurrence( cfg: dict, wt_cubes: iris.cube.Cube, data_info: dict ): - """Plot relative monthly/seasonal occurrence of weathertypes. + """Plot seasonal occurences of weathertypes. Args: - ---- - cfg : dict - Configuration dictionary from recipe - wt_cubes : iris.cube.Cube - Cube with weathertypes - data_info : dict - Dictionary with relevant info to dataset + cfg (dict): Configuration dictionary from recipe + wt_cubes (iris.cube.Cube): List of cubes of lwt, slwt_ERA5 and slwt_EOBS + data_info (dict): Dictionary with info to dataset """ driver = get_driver(data_info) @@ -97,28 +89,23 @@ def plot_seasonal_occurrence( for cube in wt_cubes: month_dict = {} for month in range(1, 13): - month_constraint = iris.Constraint( - time=iris.time.PartialDateTime(month=month) - ) - array = cube.extract(month_constraint).data + array = cube.extract( + iris.Constraint(time=iris.time.PartialDateTime(month=month)) + ).data unique, counts = np.unique(array, return_counts=True) - count_dict = dict(zip(unique, counts / sum(counts), strict=False)) - month_dict[month] = count_dict + month_dict[month] = dict( + zip(unique, counts / sum(counts), strict=False) + ) relative_occurrences[cube.long_name] = month_dict - x = month_list - for wt_string in relative_occurrences: wt_numbers = max( len(value) for value in relative_occurrences.get(wt_string).values() ) - colors = generate_grayscale_hex_values(wt_numbers) wt_stack = np.zeros((wt_numbers, 12)) for month, month_value in relative_occurrences.get(wt_string).items(): - print(month_value) for wt in month_value.keys(): - print(month_value.get(wt)) wt_stack[np.int8(wt - 1), month - 1] = month_value.get(wt) y = np.vstack(list(wt_stack)) @@ -131,7 +118,9 @@ def plot_seasonal_occurrence( {data_info.get('dataset')}, {data_info.get('timerange')}" ) - ax_.stackplot(x, y, colors=colors) + ax_.stackplot( + month_list, y, colors=generate_grayscale_hex_values(wt_numbers) + ) ax_.legend( loc="upper center", @@ -165,23 +154,16 @@ def plot_seasonal_occurrence( def plot_maps( wt: np.array, cfg: dict, cube: iris.cube.Cube, data_info: dict, mode: str ): - """Plot maps. + """Function to plot maps of means, std and anomalies. Args: - ---- - wt : np.array - weathertype number - cfg : dict - Configuration dicitonary from recipe - cube : iris.cube.Cube - Data to be plotted - data_info : dict - Dictionary with info to dataset - mode : str - Statistics that is used + wt (np.array): WT array + cfg (dict): Configuration dictionary from recipe + cube (iris.cube.Cube): Data to be plotted + data_info (dict): Dictionary with info to dataset + mode (str): Statistics that is used """ var_name = data_info.get("var") - driver = get_driver(data_info) logger.info( "Plotting %s %s %s for %s %s", @@ -192,11 +174,6 @@ def plot_maps( wt, ) - local_path = f"{cfg.get('plot_dir')}/{mode}" - - if not os.path.exists(f"{local_path}"): - os.makedirs(f"{local_path}") - ax = plt.axes(projection=ccrs.PlateCarree()) if data_info.get("var") == "psl": @@ -265,12 +242,15 @@ def plot_maps( ax.coastlines() ax.add_feature(cfeature.BORDERS, linestyle=":") + if not os.path.exists(f"{cfg.get('plot_dir')}/{mode}"): + os.makedirs(f"{cfg.get('plot_dir')}/{mode}") + plt.savefig( - f"{local_path}/{data_info.get('wt_string')}_{wt}{driver}_{data_info.get('dataset')}_{data_info.get('ensemble', '')}" + f"{cfg.get('plot_dir')}/{mode}/{data_info.get('wt_string')}_{wt}{get_driver(data_info)}_{data_info.get('dataset')}_{data_info.get('ensemble', '')}" f"_{var_name}_{mode}_{data_info.get('timerange')}.png" ) plt.savefig( - f"{local_path}/{data_info.get('wt_string')}_{wt}{driver}_{data_info.get('dataset')}_{data_info.get('ensemble', '')}_" + f"{cfg.get('plot_dir')}/{mode}/{data_info.get('wt_string')}_{wt}{get_driver(data_info)}_{data_info.get('dataset')}_{data_info.get('ensemble', '')}_" f"{var_name}_{mode}_{data_info.get('timerange')}.pdf" ) plt.close() @@ -283,20 +263,14 @@ def plot_corr_rmse_heatmaps( dataset: str, timerange: str, ): - """Plot heatmaps for correlation and rmse matrices. + """Plot correlation and rmse heatmaps. Args: - ---- - cfg : dict - Configuration dictionary from recipe - pattern_correlation_matrix : np.array - pattern correlation matrix - rmse_matrix : np.array - rmse matrix - dataset : str - string of dataset - timerange : str - string of timerange + cfg (dict): Configuration dictionary from recipe + pattern_correlation_matrix (np.array): Pattern correlation matrix + rmse_matrix (np.array): RMSE matrix + dataset (str): Name of dataset + timerange (str): Time range for the calculation """ output_path = f"{cfg.get('plot_dir')}/heatmaps" @@ -367,18 +341,13 @@ def plot_corr_rmse_heatmaps( def get_colormap(colormap_string: str) -> ListedColormap: - """Get colormaps based on string. + """Get colormaps for plottings Args: - ---- - colormap_string : str - String to get Colormaps for either - psl, tas or precipitation. - - Returns - ------- - ListedColormap - Choosen Colormap + colormap_string (str): string to identify colormap + + Returns: + ListedColormap: Colormap for the specified variable """ misc_seq_2_disc = [ (230 / 255, 240 / 255, 240 / 255), diff --git a/esmvaltool/diag_scripts/weathertyping/weathertyping.py b/esmvaltool/diag_scripts/weathertyping/weathertyping.py index b2c9c0ff13..3d1d2a1979 100644 --- a/esmvaltool/diag_scripts/weathertyping/weathertyping.py +++ b/esmvaltool/diag_scripts/weathertyping/weathertyping.py @@ -40,13 +40,9 @@ def process_models_automatic_slwt( """Process model data for calculating Lamb and simplified weathertypes. Args: - ---- - cfg : dict - Nested dictionary of metadata - dataset_vars : list - List of variable dictionaries for a specific dataset - data_info : dict - Dictionary holding dataset information. + cfg (dict): Nested dictionary of metadata + dataset_vars (list): List of variable dictionaries for a specific dataset + data_info (dict): Dictionary holding dataset information. """ for ensemble_var in dataset_vars: if ensemble_var.get("preprocessor") == "weathertype_preproc": @@ -104,15 +100,10 @@ def process_era5_automatic_slwt( """Process ERA5 data for calculating Lamb and simplified weathertypes. Args: - ----- - data_info : dict - Dictionary holding dataset information. - preproc_variables_dict : dict - Dictionary holding preprocessed variables for all datasets. - cfg : dict - Nested dictionary of metadata - dataset_vars : list - List of variable dictionaries for a specific dataset + data_info (dict): Dictionary holding dataset information. + preproc_variables_dict (dict): Dictionary holding preprocessed variables for all datasets. + cfg (dict): Nested dictionary of metadata + dataset_vars (list): List of variable dictionaries for a specific dataset """ wt_preproc, wt_preproc_prcp, wt_preproc_prcp_eobs = load_wt_preprocessors( data_info["dataset"], preproc_variables_dict @@ -180,9 +171,7 @@ def run_automatic_slwt(cfg: dict): of the weathertypes. Args: - ---- - cfg : dict - Nested dictionary of metadata + cfg (dict): Nested dictionary of metadata """ preproc_variables_dict = group_metadata( cfg.get("input_data").values(), "dataset" @@ -206,15 +195,10 @@ def process_era5_lwt(preproc_variables_dict, cfg, dataset_vars, data_info): """Process ERA5 data for calculating Lamb weathertypes. Args: - ---- - preproc_variables_dict : dict - Dictionary holding preprocessed variables for all datasets. - cfg : dict - Nested dictionary of metadata - dataset_vars : list - List of variable dictionaries for a specific dataset - data_info : dict - Dictionary holding dataset information. + preproc_variables_dict (dict): Dictionary holding preprocessed variables for all datasets. + cfg (dict): Nested dictionary of metadata + dataset_vars (list): List of variable dictionaries for a specific dataset + data_info (dict): Dictionary holding dataset information. """ wt_preproc, _, _ = load_wt_preprocessors( data_info["dataset"], preproc_variables_dict @@ -265,13 +249,9 @@ def process_models_lwt(cfg: dict, dataset_vars: list, data_info: dict): """Process model data for calculating Lamb weathertypes. Args: - ---- - cfg : dict - Nested dictionary of metadata - dataset_vars : list - List of variable dictionaries for a specific dataset - data_info : dict - Dictionary holding dataset information. + cfg (dict): Nested dictionary of metadata + dataset_vars (list): List of variable dictionaries for a specific dataset + data_info (dict): Dictionary holding dataset information. """ for ensemble_var in dataset_vars: if ensemble_var.get("preprocessor") == "weathertype_preproc": @@ -330,10 +310,7 @@ def run_lwt(cfg: dict): Plot seasonal occurrence of the weathertypes. Args: - ---- - cfg : dict - Nested dictionary of metadata - + cfg (dict): Nested dictionary of metadata """ preproc_variables_dict = group_metadata( cfg.get("input_data").values(), "dataset" @@ -358,9 +335,7 @@ def run_my_diagnostic(cfg: dict): """Run the weathertyping diagnostic. Args: - ----- - cfg : dict - nested dictionary of metadata + cfg (dict): Nested dictionary of metadata """ automatic_slwt = cfg.get("automatic_slwt") diff --git a/esmvaltool/diag_scripts/weathertyping/wt_utils.py b/esmvaltool/diag_scripts/weathertyping/wt_utils.py index ab57ab3115..03ad8e4279 100644 --- a/esmvaltool/diag_scripts/weathertyping/wt_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/wt_utils.py @@ -26,17 +26,13 @@ def get_driver(data_info: dict) -> str: - """Get driving model string. + """Get driving model name and string for further use. Args: - ---- - data_info : dict - Data information dictionary. - - Returns - ------- - str - Driver string with leading underscore or empty string. + data_info (dict): Data information dictionary. + + Returns: + str: Driver string with leading underscore or empty string. """ driver = data_info.get("driver", "") if driver != "": @@ -48,16 +44,11 @@ def load_wt_preprocessors(dataset: str, preproc_variables_dict: dict): """Load preprocessor cubes for calculating Lamb weathertypes. Args: - ---- - dataset : str - dataset name - preproc_variables_dict : dict - dictionary of preprocessor variables - - Returns - ------- - list - list of preprocessor vars for weathertyping + dataset (str): Name of dataset + preproc_variables_dict (dict): Dictionary with info on preprocessor variables + + Returns: + tuple: Preprocessor cubes for weathertyping """ wt_preproc = iris.load_cube( preproc_variables_dict.get(dataset)[0].get("filename") @@ -73,19 +64,14 @@ def load_wt_preprocessors(dataset: str, preproc_variables_dict: dict): def get_ancestors_era5_eobs(dataset: str, preproc_variables_dict: dict): - """Get ancestors for ERA5/E-OBS. + """Get ancestors for observational data. Args: - ---- - dataset : str - dataset name - preproc_variables_dict : dict - dictionary of preprocessor variables - - Returns - ------- - tuple(list, list) - lists of ERA5/E-OBS ancestors + dataset (str): Name of dataset + preproc_variables_dict (dict): Dictionary with info on preprocessor variables + + Returns: + tuple: Lists of ERA5 and E-OBS ancestors """ era5_ancestors = [ preproc_variables_dict.get(dataset)[0].get("filename"), @@ -98,47 +84,35 @@ def get_ancestors_era5_eobs(dataset: str, preproc_variables_dict: dict): return era5_ancestors, eobs_ancestors -def get_model_output_filepath(dataset: str, value: list): - """Generate output filepath for model data. +def get_model_output_filepath(dataset: str, data_info: list): + """Get output filepaths for models. Args: - ---- - dataset : str - dataset name - value : dict - Model variables - - Returns - ------- - tuple(str, str) - Output filepath and preprocessor path for - future referencing. + dataset (str): Name of dataset + data_info (list): Model variables + + Returns: + tuple: Output filepath and preprocessor path for future referencing. """ - timerange = value.get("timerange").replace("/", "-") - experiment = value.get("exp") - ensemble = value.get("ensemble") - driver = value.get("driver", "") + timerange = data_info.get("timerange").replace("/", "-") + experiment = data_info.get("exp") + ensemble = data_info.get("ensemble") + driver = data_info.get("driver", "") out_path = f"{dataset}/{driver}/{experiment}/{ensemble}/{timerange}" - preproc_path = value.get("filename") + preproc_path = data_info.get("filename") return out_path, preproc_path def get_preproc_lists(preproc_vars: list): - """Put preprocessor variables and paths into list for further use. + """Put preprocessors and paths into lists for further use. Args: - ---- - preproc_vars : list - List of variables for specific - dataset - - Returns - ------- - tuple(list, list) - List of preprocessor cubes for mean calculations - as well as path to files. + preproc_vars (list): List of preprocessor variables. + + Returns: + tuple: Preprocessor cubes and paths. """ preproc_path_psl = preproc_vars[-3].get("filename") preproc_path_prcp = preproc_vars[-2].get("filename") @@ -155,18 +129,13 @@ def get_preproc_lists(preproc_vars: list): def get_preproc_lists_ensemble(preproc_vars: list): - """Put preprocessor variables and paths into list for further use. + """Put preprocessors and paths into lists for further use. Args: - ---- - preproc_vars : list - Variable for specific ensemble member. - - Returns - ------- - tuple(iris.cube.Cube, str) - Preprocessor cube for mean calculations - as well as path to files. + preproc_vars (list): List of preprocessor variables. + + Returns: + tuple: Preprocessor cubes and paths. """ preproc_path = preproc_vars.get("filename") @@ -179,16 +148,13 @@ def get_preproc_lists_ensemble(preproc_vars: list): def get_looping_dict(preproc_vars: list): - """Put cubes into dictionary for further use. + """Put variable preprocessors into dict for looping. Args: - ---- - preproc_vars : list - Values of dataset dictionary + preproc_vars (list): List of preprocessor variables. - Returns - ------- - Dictionary of the form {'var': [preproc_var, preproc_path]} + Returns: + dict: Dictionary of preprocessor cubes and paths. """ preproc, preproc_path = get_preproc_lists(preproc_vars) dict_ = { @@ -200,23 +166,14 @@ def get_looping_dict(preproc_vars: list): def load_wt_files(path: str, only_lwt=False): - """Load *.nc files of weathertype data. - - If only_lwt is true, only Lamb - weathertypes will be loaded. (useful for automatic_slwt = False) + """Load wt files. Args: - ---- - path : str - Path to weathertype data. - only_lwt : bool, optional - If True, only Lamb weathertypes will be loaded. - Defaults to False. - - Returns - ------- - list(iris.cube.Cube) - List of weathertype cubes. + path (str): Path of wt file + only_lwt (bool, optional): If True, only Lamb weathertypes will be loaded. Defaults to False. + + Returns: + list: List of weathertype cubes. """ if not only_lwt: lwt_cube = iris.load_cube(path, "lwt") @@ -240,22 +197,14 @@ def get_provenance_record( """Get provenance record. Args: - ---- - caption : str - Caption for plots - ancestors : list - List of ancestors - long_names : list - Variable long names - plot_types : bool | list - Type of plot. Can be false if output is not a plot. - statistics : bool | list - Types of statistics used. - - Returns - ------- - dict - Provenance dictionary. + caption (str): Caption of plot + ancestors (list): List of ancestor plots + long_names (list): List of variable long names + plot_types (bool | list): Type of plot + statistics (bool | list): Types of statistics used + + Returns: + dict: Provenance record """ record = { "caption": caption, @@ -274,16 +223,12 @@ def get_provenance_record( def log_provenance(filename: str, cfg: dict, provenance_record: dict): - """Log provenance. Produces xml file provenance info. + """Log provenance. Args: - ---- - filename : str - Output name of provenance. - cfg : dict - Configuration dictionary provided by recipe. - provenance_record : dict - Provenance record dictionary. + filename (str): Filename of xml file + cfg (dict): Configuration dictionary + provenance_record (dict): Provenance record dictionary """ with ProvenanceLogger(cfg) as provenance_logger: provenance_logger.log(filename, provenance_record) @@ -292,17 +237,13 @@ def log_provenance(filename: str, cfg: dict, provenance_record: dict): def turn_list_to_mapping_dict(list_: list) -> dict: - """Turn list of combined WT into a dictionary for further processing. + """Turn list of combined WT to a dictionary for further use. Args: - ---- - list_ : list - List where entries are lists with related WT - - Returns - ------- - dict - Mapping dictionary keys are simplified WT, values are Lamb WT + list_ (list): List of combined WTs + + Returns: + dict: Dictionary of combined WTs """ result_dict = {} @@ -320,16 +261,10 @@ def get_mapping_dict(selected_pairs: list) -> dict: """Get mapping dictionary from list of selected pairs. Args: - ---- - selected_pairs : list - List of selected pairs of related WT based on - precipitation patterns over specified area and - correlation and RSME thresholds defined in recipe - - Returns - ------- - dict - Mapping dicitonary keys are simplified WT, values are Lamb WT + selected_pairs (list): List of selected weathertype pairs + + Returns: + dict: Mapping dictionary """ mapping_array = [] @@ -356,13 +291,9 @@ def write_mapping_dict(work_dir: str, dataset: str, mapping_dict: dict): """Write mapping dictionary to file. Args: - ---- - work_dir : str - Working directory to save mapping dict - dataset : str - Name of dataset - mapping_dict : dict - Mapping dictionary in {lwt: slwt, ...} format + work_dir (str): Current working directory + dataset (str): Dataset name + mapping_dict (dict): Mapping dictionary in {lwt: slwt, ...} format """ mapping_dict_reformat = convert_dict(mapping_dict) @@ -373,19 +304,14 @@ def write_mapping_dict(work_dir: str, dataset: str, mapping_dict: dict): def convert_dict(dict_: dict) -> dict: - """Convert mapping dictionary. + """Convert dictionary from {lwt: slwt, ...} format to {slwt: [lwt1, lwt2], ...}. - From {lwt: slwt, ...} format to {slwt: [lwt1, lwt2], ...}. Args: - ---- - dict_ : dict - Dict in the {lwt: slwt, ...} format - - Returns - ------- - dict - Dict in the {slwt: [lwt1, lwt2], ...} format + dict_ (dict): Mapping dictionary to be converted + + Returns: + dict: Converted dictionary """ new_dict = {} for dataset, value in dict_.items(): @@ -401,14 +327,10 @@ def reverse_convert_dict(dict_: dict) -> dict: From {slwt: [lwt1, lwt2], ...} format to {lwt: slwt, ...}. Args: - ---- - original_dict : dict - Dict in the {slwt: [lwt1, lwt2], ...} format - - Returns - ------- - dict - Dict in the format {lwt: slwt, ...} + original_dict (dict): Dict in the {slwt: [lwt1, lwt2], ...} format + + Returns: + dict: Dict in the format {lwt: slwt, ...} """ new_dict = {} for key, value_list in dict_.items(): @@ -426,15 +348,10 @@ def write_corr_rmse_to_csv( """Write correlation and rsme matrix to csv files. Args: - ---- - cfg : dict - Configuration dictionary from recipe - pattern_correlation_matrix : np.array - Correlation matrix - rmse_matrix : np.array - RSME matrix - dataset : str - Name of dataset + cfg (dict): Configuration dictionary from recipe + pattern_correlation_matrix (np.array): Correlation matrix + rmse_matrix (np.array): RSME matrix + dataset (str): Name of dataset """ logger.info("Writing corr and rsme matrices for %s", dataset) @@ -461,22 +378,14 @@ def run_predefined_slwt( """Run predefined slwt mapping. Args: - ---- - work_dir : str - Working directory to save mapping dict - dataset_name : str - Name of dataset - lwt : np.array - lwt array - predefined_slwt : dict - Mapping dictionary in {lwt: slwt, ...} format - - Returns - ------- - np.array - slwt_era5 array - np.array - slwt_eobs array + work_dir (str): Working directory to save mapping dict + dataset_name (str): Name of dataset + lwt (np.array): lwt array + predefined_slwt (dict): Mapping dictionary in {lwt: slwt, ...} format + + Returns: + np.array: slwt_era5 array + np.array: slwt_eobs array """ predefined_slwt = check_mapping_dict_format(predefined_slwt) write_mapping_dict(work_dir, dataset_name, predefined_slwt) @@ -493,15 +402,10 @@ def combine_wt_to_file( """Combine lwt and slwt arrays to one file. Args: - ---- - cfg : dict - Configuration dictionary from recipe - wt_list : list - List of weathertype arrays - cube : iris.cube.Cube - Cube of data to keep time coordinate - file_name : str - Name of output file + cfg (dict): Configuration dictionary from recipe + wt_list (list): List of weathertype arrays + cube (iris.cube.Cube): Cube of data to keep time coordinate + file_name (str): Name of output file """ lwt = wt_list[0] slwt_era5 = wt_list[1] @@ -542,15 +446,10 @@ def write_lwt_to_file( """Write only lwt to file. Args: - ---- - cfg : dict - Configuration dictionary from recipe - lwt : np.array - lwt array - cube : iris.cube.Cube - Cube of data to keep time coordinate - file_name : str - Name of output file + cfg (dict): Configuration dictionary from recipe + lwt (np.array): lwt array + cube (iris.cube.Cube): Cube of data to keep time coordinate + file_name (str): Name of output file """ logger.info("Writing Lamb Weathertype to %s", file_name) @@ -575,16 +474,11 @@ def map_lwt_to_slwt(lwt: np.array, mapping_dict: dict) -> np.array: """Map lwt array to slwt array. Args: - ---- - lwt : np.array - lwt array - mapping_dict : dict - Mapping dictionary in {lwt: slwt, ...} format - - Returns - ------- - np.array - array of slwt + lwt (np.array): lwt array + mapping_dict (dict): Mapping dictionary in {lwt: slwt, ...} format + + Returns: + np.array: array of slwt """ return np.array([np.int8(mapping_dict.get(value, 0)) for value in lwt]) @@ -593,14 +487,10 @@ def check_mapping_dict_format(mapping_dict: dict) -> dict: """Check format of mapping dict and return in {lwt: slwt, ...} format. Args: - ---- - mapping_dict : dict - mapping dict in any format - - Returns - ------- - dict - mapping dict in {lwt: slwt, ...} format + mapping_dict (dict): mapping dict in any format + + Returns: + dict: mapping dict in {lwt: slwt, ...} format """ if isinstance(mapping_dict.get(list(mapping_dict.keys())[0]), list): dict_ = reverse_convert_dict(mapping_dict) From a13c03451bf8aa352fb285caf7647b56ad76dfc6 Mon Sep 17 00:00:00 2001 From: Thomas Kroissenbrunner Date: Tue, 4 Nov 2025 11:15:50 +0100 Subject: [PATCH 10/57] doc --- esmvaltool/diag_scripts/weathertyping/calc_utils.py | 2 +- esmvaltool/diag_scripts/weathertyping/wt_utils.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/esmvaltool/diag_scripts/weathertyping/calc_utils.py b/esmvaltool/diag_scripts/weathertyping/calc_utils.py index 769771ee8f..1681d396cf 100644 --- a/esmvaltool/diag_scripts/weathertyping/calc_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/calc_utils.py @@ -896,7 +896,7 @@ def plot_means( data_info: dict, only_lwt=False, ): - """Wrapper function for plotting means, anomalies and standard deviations. + """Function for plotting means, anomalies and standard deviations. Args: cfg (dict): Configuration dictionary from recipe diff --git a/esmvaltool/diag_scripts/weathertyping/wt_utils.py b/esmvaltool/diag_scripts/weathertyping/wt_utils.py index 03ad8e4279..e1749560d4 100644 --- a/esmvaltool/diag_scripts/weathertyping/wt_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/wt_utils.py @@ -477,7 +477,7 @@ def map_lwt_to_slwt(lwt: np.array, mapping_dict: dict) -> np.array: lwt (np.array): lwt array mapping_dict (dict): Mapping dictionary in {lwt: slwt, ...} format - Returns: + Returns np.array: array of slwt """ return np.array([np.int8(mapping_dict.get(value, 0)) for value in lwt]) @@ -489,7 +489,8 @@ def check_mapping_dict_format(mapping_dict: dict) -> dict: Args: mapping_dict (dict): mapping dict in any format - Returns: + Returns + -------- dict: mapping dict in {lwt: slwt, ...} format """ if isinstance(mapping_dict.get(list(mapping_dict.keys())[0]), list): From d4a037442d09dd0fb0e258189816275b5c802ac7 Mon Sep 17 00:00:00 2001 From: Thomas Kroissenbrunner Date: Tue, 4 Nov 2025 11:23:38 +0100 Subject: [PATCH 11/57] doc --- .../diag_scripts/weathertyping/calc_utils.py | 31 ++++++++---- .../diag_scripts/weathertyping/plot_utils.py | 6 ++- .../weathertyping/weathertyping.py | 7 +++ .../diag_scripts/weathertyping/wt_utils.py | 48 +++++++++++++------ 4 files changed, 67 insertions(+), 25 deletions(-) diff --git a/esmvaltool/diag_scripts/weathertyping/calc_utils.py b/esmvaltool/diag_scripts/weathertyping/calc_utils.py index 1681d396cf..8aef30e760 100644 --- a/esmvaltool/diag_scripts/weathertyping/calc_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/calc_utils.py @@ -54,7 +54,8 @@ def calc_slwt_obs( ancestors (list): List of ancestors timerange (str): Time range for the calculation - Returns: + Returns + ------- np.array: Simplified Lamb Weathertypes """ logger.info("Calculating simplified Lamb Weathertypes for %s", dataset) @@ -121,7 +122,8 @@ def calc_const(): scheme. Int. J. Climatol., 13: 655-663. https://doi.org/10.1002/joc.3370130606 - Returns: + Returns + ------- tuple: The four constants needed for WT calculation. """ const1 = 1 / np.cos(45 * np.pi / 180) @@ -143,7 +145,8 @@ def calc_westerly_flow(cube: iris.cube.Cube) -> np.array: Args: cube (iris.cube.Cube): Cube of psl data. - Returns: + Returns + ------- np.array: westerly flow """ return 1 / 2 * (cube.data[:, 1, 2] + cube.data[:, 1, 4]) - 1 / 2 * ( @@ -163,7 +166,8 @@ def calc_southerly_flow(cube: iris.cube.Cube, const1: float) -> np.array: cube (iris.cube.Cube): cube of psl data const1 (float): const1 - Returns: + Returns + ------- np.array: southerly flow """ return const1 * ( @@ -187,7 +191,6 @@ def calc_resultant_flow( Int. J. Climatol., 13: 655-663. https://doi.org/10.1002/joc.3370130606 Args: - ---- westerly_flow (np.array): westerly flow southerly_flow (np.array): southerly flow @@ -209,7 +212,6 @@ def calc_westerly_shear_velocity( Int. J. Climatol., 13: 655-663. https://doi.org/10.1002/joc.3370130606 Args: - ---- cube (iris.cube.Cube): cube of psl data const2 (float): const2 const3 (float): const3 @@ -238,7 +240,6 @@ def calc_southerly_shear_velocity( Int. J. Climatol., 13: 655-663. https://doi.org/10.1002/joc.3370130606 Args: - ---- cube (iris.cube.Cube): cube of psl data const4 (float): const @@ -277,6 +278,7 @@ def calc_total_shear_velocity( southerly_shear_velocity (np.array): southerly shear velocity Returns + ------- np.array:total shear velocity """ return westerly_shear_velocity + southerly_shear_velocity @@ -294,7 +296,8 @@ def wt_algorithm(cube: iris.cube.Cube, dataset: str) -> np.array: cube (iris.cube.Cube): Cube of psl data dataset (str): Name of dataset - Returns: + Returns + ------- np.array: Lamb weathertypes """ @@ -430,6 +433,7 @@ def calc_lwt_slwt_model( """Calculate Lamb as well as simplified weathertypes for model. Args: + ------- cfg (dict): Configuration dictionary from recipe cube (iris.cube.Cube): PSL field of dataset data_info (dict): Dictionary with info to dataset @@ -544,7 +548,8 @@ def process_prcp_mean( dataset (str): Name of dataset timerange (str): Time range of dataset - Returns: + Returns + ------- list: Selected pairs of WT. This is passed to get_mapping_dict """ logger.info("Calculating corr and rsme matrices for %s", dataset) @@ -590,6 +595,7 @@ def calc_wt_means( """Calculate means for psl, tas or pr for weathertypes. Args: + ------- cfg (dict): Configuration dictionary from recipe cube (iris.cube.Cube): Cube with variable data wt_cubes (iris.cube.CubeList): List of cubes of lwt, slwt_ERA5 and slwt_EOBS @@ -662,7 +668,8 @@ def get_wt_array(wt_string: str, wt_cubes: iris.cube.CubeList) -> np.array: Raises: NameError: if wt_array does not exist for the given wt_string - Returns: + Returns + ------- (np.array, np.array): weathertype array and time coordinate """ if wt_string == "slwt_ERA5": @@ -694,6 +701,7 @@ def calc_wt_anomalies( """Calculate anomalies for psl, tas and pr for weathertypes. Args: + ------- cfg (dict): Configuration dictionary from recipe cube (iris.cube.Cube): Cube with variable data wt_cubes (iris.cube.CubeList): List of cubes of lwt, slwt_ERA5 and slwt_EOBS @@ -766,6 +774,7 @@ def calc_wt_std( """Calculate standard deviation for psl, tas and pr for weathertypes. Args: + ------- cfg (dict): Configuration dictionary from recipe cube (iris.cube.Cube): Cube with variable data wt_cubes (iris.cube.CubeList): List of cubes of lwt, slwt_ERA5 and slwt_EOBS @@ -830,6 +839,7 @@ def calc_lwt_model(cfg: dict, cube: iris.cube.Cube, data_info: dict): """Calculate Lamb weathertypes for models. Args: + ------- cfg (dict): Configuration dictionary from recipe cube (iris.cube.Cube): Cube with variable data data_info (dict): Dictionary with info to dataset @@ -899,6 +909,7 @@ def plot_means( """Function for plotting means, anomalies and standard deviations. Args: + ------- cfg (dict): Configuration dictionary from recipe preproc_var (np.array): Preprocessed variable cube wt_cubes (iris.cube.Cube): List of cubes of lwt, slwt_ERA5 and slwt_EOBS diff --git a/esmvaltool/diag_scripts/weathertyping/plot_utils.py b/esmvaltool/diag_scripts/weathertyping/plot_utils.py index 56a496e6a0..4ddc2aa8cb 100644 --- a/esmvaltool/diag_scripts/weathertyping/plot_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/plot_utils.py @@ -57,6 +57,7 @@ def plot_seasonal_occurrence( """Plot seasonal occurences of weathertypes. Args: + ------- cfg (dict): Configuration dictionary from recipe wt_cubes (iris.cube.Cube): List of cubes of lwt, slwt_ERA5 and slwt_EOBS data_info (dict): Dictionary with info to dataset @@ -157,6 +158,7 @@ def plot_maps( """Function to plot maps of means, std and anomalies. Args: + ------- wt (np.array): WT array cfg (dict): Configuration dictionary from recipe cube (iris.cube.Cube): Data to be plotted @@ -266,6 +268,7 @@ def plot_corr_rmse_heatmaps( """Plot correlation and rmse heatmaps. Args: + ------- cfg (dict): Configuration dictionary from recipe pattern_correlation_matrix (np.array): Pattern correlation matrix rmse_matrix (np.array): RMSE matrix @@ -346,7 +349,8 @@ def get_colormap(colormap_string: str) -> ListedColormap: Args: colormap_string (str): string to identify colormap - Returns: + Returns + ------- ListedColormap: Colormap for the specified variable """ misc_seq_2_disc = [ diff --git a/esmvaltool/diag_scripts/weathertyping/weathertyping.py b/esmvaltool/diag_scripts/weathertyping/weathertyping.py index 3d1d2a1979..49f553afaa 100644 --- a/esmvaltool/diag_scripts/weathertyping/weathertyping.py +++ b/esmvaltool/diag_scripts/weathertyping/weathertyping.py @@ -40,6 +40,7 @@ def process_models_automatic_slwt( """Process model data for calculating Lamb and simplified weathertypes. Args: + ------- cfg (dict): Nested dictionary of metadata dataset_vars (list): List of variable dictionaries for a specific dataset data_info (dict): Dictionary holding dataset information. @@ -100,6 +101,7 @@ def process_era5_automatic_slwt( """Process ERA5 data for calculating Lamb and simplified weathertypes. Args: + ------- data_info (dict): Dictionary holding dataset information. preproc_variables_dict (dict): Dictionary holding preprocessed variables for all datasets. cfg (dict): Nested dictionary of metadata @@ -171,6 +173,7 @@ def run_automatic_slwt(cfg: dict): of the weathertypes. Args: + ------- cfg (dict): Nested dictionary of metadata """ preproc_variables_dict = group_metadata( @@ -195,6 +198,7 @@ def process_era5_lwt(preproc_variables_dict, cfg, dataset_vars, data_info): """Process ERA5 data for calculating Lamb weathertypes. Args: + ------- preproc_variables_dict (dict): Dictionary holding preprocessed variables for all datasets. cfg (dict): Nested dictionary of metadata dataset_vars (list): List of variable dictionaries for a specific dataset @@ -249,6 +253,7 @@ def process_models_lwt(cfg: dict, dataset_vars: list, data_info: dict): """Process model data for calculating Lamb weathertypes. Args: + ------- cfg (dict): Nested dictionary of metadata dataset_vars (list): List of variable dictionaries for a specific dataset data_info (dict): Dictionary holding dataset information. @@ -310,6 +315,7 @@ def run_lwt(cfg: dict): Plot seasonal occurrence of the weathertypes. Args: + ------- cfg (dict): Nested dictionary of metadata """ preproc_variables_dict = group_metadata( @@ -335,6 +341,7 @@ def run_my_diagnostic(cfg: dict): """Run the weathertyping diagnostic. Args: + ------- cfg (dict): Nested dictionary of metadata """ automatic_slwt = cfg.get("automatic_slwt") diff --git a/esmvaltool/diag_scripts/weathertyping/wt_utils.py b/esmvaltool/diag_scripts/weathertyping/wt_utils.py index e1749560d4..92e3b0a67a 100644 --- a/esmvaltool/diag_scripts/weathertyping/wt_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/wt_utils.py @@ -31,7 +31,8 @@ def get_driver(data_info: dict) -> str: Args: data_info (dict): Data information dictionary. - Returns: + Returns + ------- str: Driver string with leading underscore or empty string. """ driver = data_info.get("driver", "") @@ -47,7 +48,8 @@ def load_wt_preprocessors(dataset: str, preproc_variables_dict: dict): dataset (str): Name of dataset preproc_variables_dict (dict): Dictionary with info on preprocessor variables - Returns: + Returns + ------- tuple: Preprocessor cubes for weathertyping """ wt_preproc = iris.load_cube( @@ -70,7 +72,8 @@ def get_ancestors_era5_eobs(dataset: str, preproc_variables_dict: dict): dataset (str): Name of dataset preproc_variables_dict (dict): Dictionary with info on preprocessor variables - Returns: + Returns + ------- tuple: Lists of ERA5 and E-OBS ancestors """ era5_ancestors = [ @@ -91,7 +94,8 @@ def get_model_output_filepath(dataset: str, data_info: list): dataset (str): Name of dataset data_info (list): Model variables - Returns: + Returns + ------- tuple: Output filepath and preprocessor path for future referencing. """ timerange = data_info.get("timerange").replace("/", "-") @@ -111,7 +115,8 @@ def get_preproc_lists(preproc_vars: list): Args: preproc_vars (list): List of preprocessor variables. - Returns: + Returns + ------- tuple: Preprocessor cubes and paths. """ preproc_path_psl = preproc_vars[-3].get("filename") @@ -134,7 +139,8 @@ def get_preproc_lists_ensemble(preproc_vars: list): Args: preproc_vars (list): List of preprocessor variables. - Returns: + Returns + ------- tuple: Preprocessor cubes and paths. """ preproc_path = preproc_vars.get("filename") @@ -153,7 +159,8 @@ def get_looping_dict(preproc_vars: list): Args: preproc_vars (list): List of preprocessor variables. - Returns: + Returns + ------- dict: Dictionary of preprocessor cubes and paths. """ preproc, preproc_path = get_preproc_lists(preproc_vars) @@ -172,7 +179,8 @@ def load_wt_files(path: str, only_lwt=False): path (str): Path of wt file only_lwt (bool, optional): If True, only Lamb weathertypes will be loaded. Defaults to False. - Returns: + Returns + ------- list: List of weathertype cubes. """ if not only_lwt: @@ -203,7 +211,8 @@ def get_provenance_record( plot_types (bool | list): Type of plot statistics (bool | list): Types of statistics used - Returns: + Returns + ------- dict: Provenance record """ record = { @@ -226,6 +235,7 @@ def log_provenance(filename: str, cfg: dict, provenance_record: dict): """Log provenance. Args: + ------- filename (str): Filename of xml file cfg (dict): Configuration dictionary provenance_record (dict): Provenance record dictionary @@ -242,7 +252,8 @@ def turn_list_to_mapping_dict(list_: list) -> dict: Args: list_ (list): List of combined WTs - Returns: + Returns + ------- dict: Dictionary of combined WTs """ result_dict = {} @@ -263,7 +274,8 @@ def get_mapping_dict(selected_pairs: list) -> dict: Args: selected_pairs (list): List of selected weathertype pairs - Returns: + Returns + ------- dict: Mapping dictionary """ mapping_array = [] @@ -291,6 +303,7 @@ def write_mapping_dict(work_dir: str, dataset: str, mapping_dict: dict): """Write mapping dictionary to file. Args: + ------- work_dir (str): Current working directory dataset (str): Dataset name mapping_dict (dict): Mapping dictionary in {lwt: slwt, ...} format @@ -310,7 +323,8 @@ def convert_dict(dict_: dict) -> dict: Args: dict_ (dict): Mapping dictionary to be converted - Returns: + Returns + ------- dict: Converted dictionary """ new_dict = {} @@ -329,7 +343,8 @@ def reverse_convert_dict(dict_: dict) -> dict: Args: original_dict (dict): Dict in the {slwt: [lwt1, lwt2], ...} format - Returns: + Returns + ------- dict: Dict in the format {lwt: slwt, ...} """ new_dict = {} @@ -348,6 +363,7 @@ def write_corr_rmse_to_csv( """Write correlation and rsme matrix to csv files. Args: + ------- cfg (dict): Configuration dictionary from recipe pattern_correlation_matrix (np.array): Correlation matrix rmse_matrix (np.array): RSME matrix @@ -383,7 +399,8 @@ def run_predefined_slwt( lwt (np.array): lwt array predefined_slwt (dict): Mapping dictionary in {lwt: slwt, ...} format - Returns: + Returns + ------- np.array: slwt_era5 array np.array: slwt_eobs array """ @@ -402,6 +419,7 @@ def combine_wt_to_file( """Combine lwt and slwt arrays to one file. Args: + ------- cfg (dict): Configuration dictionary from recipe wt_list (list): List of weathertype arrays cube (iris.cube.Cube): Cube of data to keep time coordinate @@ -446,6 +464,7 @@ def write_lwt_to_file( """Write only lwt to file. Args: + ------- cfg (dict): Configuration dictionary from recipe lwt (np.array): lwt array cube (iris.cube.Cube): Cube of data to keep time coordinate @@ -478,6 +497,7 @@ def map_lwt_to_slwt(lwt: np.array, mapping_dict: dict) -> np.array: mapping_dict (dict): Mapping dictionary in {lwt: slwt, ...} format Returns + -------- np.array: array of slwt """ return np.array([np.int8(mapping_dict.get(value, 0)) for value in lwt]) From 0523ccba975d1f2f6f32ff394770ab05dcc8d025 Mon Sep 17 00:00:00 2001 From: Thomas Kroissenbrunner Date: Fri, 28 Nov 2025 14:03:45 +0100 Subject: [PATCH 12/57] update --- .codacy/cli.sh | 149 ++++++++++++++++ .codacy/codacy.yaml | 4 + .github/copilot-instructions.md | 165 ++++++++++++++++++ .../diag_scripts/weathertyping/calc_utils.py | 19 +- .../diag_scripts/weathertyping/plot_utils.py | 16 +- .../weathertyping/weathertyping.py | 48 ++--- .../diag_scripts/weathertyping/wt_utils.py | 57 +++--- 7 files changed, 398 insertions(+), 60 deletions(-) create mode 100755 .codacy/cli.sh create mode 100644 .codacy/codacy.yaml create mode 100644 .github/copilot-instructions.md diff --git a/.codacy/cli.sh b/.codacy/cli.sh new file mode 100755 index 0000000000..d5438cdd31 --- /dev/null +++ b/.codacy/cli.sh @@ -0,0 +1,149 @@ +#!/usr/bin/env bash + + +set -e +o pipefail + +# Set up paths first +bin_name="codacy-cli-v2" + +# Determine OS-specific paths +os_name=$(uname) +arch=$(uname -m) + +case "$arch" in +"x86_64") + arch="amd64" + ;; +"x86") + arch="386" + ;; +"aarch64"|"arm64") + arch="arm64" + ;; +esac + +if [ -z "$CODACY_CLI_V2_TMP_FOLDER" ]; then + if [ "$(uname)" = "Linux" ]; then + CODACY_CLI_V2_TMP_FOLDER="$HOME/.cache/codacy/codacy-cli-v2" + elif [ "$(uname)" = "Darwin" ]; then + CODACY_CLI_V2_TMP_FOLDER="$HOME/Library/Caches/Codacy/codacy-cli-v2" + else + CODACY_CLI_V2_TMP_FOLDER=".codacy-cli-v2" + fi +fi + +version_file="$CODACY_CLI_V2_TMP_FOLDER/version.yaml" + + +get_version_from_yaml() { + if [ -f "$version_file" ]; then + local version=$(grep -o 'version: *"[^"]*"' "$version_file" | cut -d'"' -f2) + if [ -n "$version" ]; then + echo "$version" + return 0 + fi + fi + return 1 +} + +get_latest_version() { + local response + if [ -n "$GH_TOKEN" ]; then + response=$(curl -Lq --header "Authorization: Bearer $GH_TOKEN" "https://api.github.com/repos/codacy/codacy-cli-v2/releases/latest" 2>/dev/null) + else + response=$(curl -Lq "https://api.github.com/repos/codacy/codacy-cli-v2/releases/latest" 2>/dev/null) + fi + + handle_rate_limit "$response" + local version=$(echo "$response" | grep -m 1 tag_name | cut -d'"' -f4) + echo "$version" +} + +handle_rate_limit() { + local response="$1" + if echo "$response" | grep -q "API rate limit exceeded"; then + fatal "Error: GitHub API rate limit exceeded. Please try again later" + fi +} + +download_file() { + local url="$1" + + echo "Downloading from URL: ${url}" + if command -v curl > /dev/null 2>&1; then + curl -# -LS "$url" -O + elif command -v wget > /dev/null 2>&1; then + wget "$url" + else + fatal "Error: Could not find curl or wget, please install one." + fi +} + +download() { + local url="$1" + local output_folder="$2" + + ( cd "$output_folder" && download_file "$url" ) +} + +download_cli() { + # OS name lower case + suffix=$(echo "$os_name" | tr '[:upper:]' '[:lower:]') + + local bin_folder="$1" + local bin_path="$2" + local version="$3" + + if [ ! -f "$bin_path" ]; then + echo "📥 Downloading CLI version $version..." + + remote_file="codacy-cli-v2_${version}_${suffix}_${arch}.tar.gz" + url="https://github.com/codacy/codacy-cli-v2/releases/download/${version}/${remote_file}" + + download "$url" "$bin_folder" + tar xzfv "${bin_folder}/${remote_file}" -C "${bin_folder}" + fi +} + +# Warn if CODACY_CLI_V2_VERSION is set and update is requested +if [ -n "$CODACY_CLI_V2_VERSION" ] && [ "$1" = "update" ]; then + echo "⚠️ Warning: Performing update with forced version $CODACY_CLI_V2_VERSION" + echo " Unset CODACY_CLI_V2_VERSION to use the latest version" +fi + +# Ensure version.yaml exists and is up to date +if [ ! -f "$version_file" ] || [ "$1" = "update" ]; then + echo "ℹ️ Fetching latest version..." + version=$(get_latest_version) + mkdir -p "$CODACY_CLI_V2_TMP_FOLDER" + echo "version: \"$version\"" > "$version_file" +fi + +# Set the version to use +if [ -n "$CODACY_CLI_V2_VERSION" ]; then + version="$CODACY_CLI_V2_VERSION" +else + version=$(get_version_from_yaml) +fi + + +# Set up version-specific paths +bin_folder="${CODACY_CLI_V2_TMP_FOLDER}/${version}" + +mkdir -p "$bin_folder" +bin_path="$bin_folder"/"$bin_name" + +# Download the tool if not already installed +download_cli "$bin_folder" "$bin_path" "$version" +chmod +x "$bin_path" + +run_command="$bin_path" +if [ -z "$run_command" ]; then + fatal "Codacy cli v2 binary could not be found." +fi + +if [ "$#" -eq 1 ] && [ "$1" = "download" ]; then + echo "Codacy cli v2 download succeeded" +else + eval "$run_command $*" +fi diff --git a/.codacy/codacy.yaml b/.codacy/codacy.yaml new file mode 100644 index 0000000000..c2cccd0da6 --- /dev/null +++ b/.codacy/codacy.yaml @@ -0,0 +1,4 @@ +runtimes: + - node@22.2.0 +tools: + - eslint@8.57.0 diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000000..80925ee934 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,165 @@ +## Quick context for AI code contributors + +This repository contains ESMValTool — a community climate-model diagnostics and +preprocessing framework. The goal of these notes is to give an AI agent the +minimum, high-value knowledge to be productive immediately: how the project is +structured, common developer workflows, and concrete examples to follow. + +### Quick setup (developer environment) + +- Prefer Conda/Mamba using the provided environment files at the repository root: + +```bash +# create the dev environment (mamba recommended) +mamba env create --name esmvaltool --file environment.yml +conda activate esmvaltool +# or use environment_python.yml for a lighter python-only env +``` + +- Docker: the repository includes `docker/Dockerfile`. Build from the repo root: + +```bash +docker build -t esmvaltool:latest . -f docker/Dockerfile +``` + +- Installation for local development: pip install editable mode and tests extras + +```bash +pip install -e .[test] +pytest # run tests (pytest options are configured in pyproject.toml) +``` + +### Where to run diagnostics / recipes + +- Recipes and diagnostics are described in the README and docs. Diagnostics + live under `esmvaltool/diag_scripts/`. Example diagnostic: + `esmvaltool/diag_scripts/weathertyping/weathertyping.py`. + +### Architecture & major components (short) + +- `esmvaltool/` — main package. Diagnostics and helper code live here. + - `diag_scripts/` — each diagnostic in its own sub-folder. Diagnostics are + Python scripts but may call NCL/R tools. + - `cmorizers/` — scripts to convert observational datasets to CMOR format. +- `ESMValTool_stuff/`, `preval/` — user recipes, examples, and auxiliary + analysis code. Keep changes to these under separate branches unless they are + core fixes. +- External integration: heavy reliance on `ESMValCore` (preprocessing), Iris + (`scitools-iris`) for cube handling, and many scientific packages (see + `pyproject.toml` and `environment.yml`). Changes to dataflow must respect + ESMValCore's preprocessing contracts. + +### Diagnostic implementation conventions (concrete patterns) + +- Entry pattern: diagnostics obtain config via the shared context manager: + +```python +from esmvaltool.diag_scripts.shared import run_diagnostic + +if __name__ == '__main__': + with run_diagnostic() as config: + run_my_diagnostic(config) +``` + +Follow the `weathertyping` example: functions take the `cfg` dict returned by +`run_diagnostic()` and extract preprocessor variables through helper functions +in the same diagnostic package. + +- File I/O: diagnostics generally read/write netCDF files (Iris cubes). Use the + existing helper utilities in `diag_scripts//` (e.g., `wt_utils`, + `calc_utils`) instead of re-implementing low-level file-handling. + +### Testing, linting and formatting specifics + +- Tests: configured through `pyproject.toml`. After `pip install -e .[test]` run + `pytest`. The project uses 79-character line length. +- Linting: `ruff` + `prospector` (see `pyproject.toml`). Prefer automatic fixes + where safe (the repo config sets `fix = true` for ruff). + +### Packaging and runtime notes to keep in mind + +- The package expects Python >= 3.11 (see `pyproject.toml`). +- `esmvaltool/__init__.py` raises a helpful error if the package is not installed + editable — prefer `pip install -e .` for local imports and tests. +- Many dependencies come from conda-forge and some (e.g., ESMPy) are not on + PyPI. For CI-like runs prefer the provided `environment.yml` or the Docker + image. + +### Helpful repository-specific search patterns for agents + +- Diagnostics: `esmvaltool/diag_scripts/**/` — look for `run_diagnostic` use. +- Recipes/user examples: `ESMValTool_stuff/` and `ESMValTool_output/`. +- Environment and build: `environment.yml`, `environment_python.yml`, + `docker/Dockerfile`, `pyproject.toml`. + +### What to change and what to avoid + +- Safe: small diagnostic improvements, refactors within a diagnostic folder, + tests for new behavior, documentation updates to README/docs snippets. +- Avoid: large changes to preprocessing contracts, switching major dependency + versions, or edits to recipes in `ESMValTool_stuff/` that alter user data + provenance without discussing with maintainers. + +### Quick examples to cite in PRs + +- Point reviewers to `esmvaltool/diag_scripts/weathertyping/weathertyping.py` + as a model for diagnostic layout and `docker/Dockerfile` and + `environment.yml` for environment reproducibility. + +### When to ask a human + +- If a change affects: the public API of preprocessing (ESMValCore contracts), + Docker/CI pipelines, or requires new conda-build recipes. Also ask if you + need access to large datasets (CMIP/obs) that are not in the repo. + +If anything here is unclear or you'd like a shorter/longer variant, tell me +which parts to expand and I'll iterate. + + +# Copilot Agents for VSCode + +## Auto-Fix Codacy Minor Issues Agent + +This agent is designed to assist developers by automatically fixing **Codacy minor issues** in code, using GitHub Copilot suggestions within VSCode. + +### Overview + +- **Purpose:** Automatically detect and fix minor code quality issues flagged by Codacy. +- **Scope:** Minor issues only (e.g., formatting, unused imports, variable naming, unnecessary semicolons). +- **Toolchain:** VSCode + GitHub Copilot + Codacy CLI (optional for local linting). +- **Trigger:** On file save or on-demand. + +### Setup + +1. **Install VSCode Extensions** + - [GitHub Copilot](https://marketplace.visualstudio.com/items?itemName=GitHub.copilot) + - [Codacy VSCode Extension](https://marketplace.visualstudio.com/items?itemName=Codacy.codacy) + - Optional: Linter/formatter for your language (e.g., ESLint, Prettier, Black). + +2. **Enable Copilot Suggestions** + - Go to VSCode `Settings` → `Copilot` → Enable inline suggestions. + - Enable `Accept Copilot suggestions on save` for automated workflow (optional, may require keybinding). + +3. **Codacy CLI (optional)** + - Install Codacy CLI: + ```bash + npm install -g @codacy/cli + ``` + - Run to check issues locally: + ```bash + codacy analyze + ``` + +### Workflow + +1. Open your project in VSCode. +2. Open the file flagged with minor Codacy issues. +3. Accept Copilot inline suggestions to automatically fix formatting, variable names, or other minor issues. +4. Save the file to trigger any auto-formatters configured. +5. Run Codacy analysis to ensure issues are resolved. + +### Example Use Cases + +- **Unused import:** + ```python + import os # Minor issue: unused import diff --git a/esmvaltool/diag_scripts/weathertyping/calc_utils.py b/esmvaltool/diag_scripts/weathertyping/calc_utils.py index 8aef30e760..663ddfda9e 100644 --- a/esmvaltool/diag_scripts/weathertyping/calc_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/calc_utils.py @@ -430,7 +430,7 @@ def calc_lwt_slwt_model( data_info: dict, predefined_slwt: bool | dict, ): - """Calculate Lamb as well as simplified weathertypes for model. + """Calculate Lamb as well as simplified weathertypes for model and write them to file. Args: ------- @@ -543,6 +543,7 @@ def process_prcp_mean( """Return which weathertypes can be grouped together for a certain precipitation pattern. Args: + ---- cfg (dict): Configuration dictionary from recipe data (np.array): Array of precipitation means for each WT dataset (str): Name of dataset @@ -595,7 +596,7 @@ def calc_wt_means( """Calculate means for psl, tas or pr for weathertypes. Args: - ------- + ---- cfg (dict): Configuration dictionary from recipe cube (iris.cube.Cube): Cube with variable data wt_cubes (iris.cube.CubeList): List of cubes of lwt, slwt_ERA5 and slwt_EOBS @@ -662,10 +663,12 @@ def get_wt_array(wt_string: str, wt_cubes: iris.cube.CubeList) -> np.array: """Get weathertype array and time coordinate based on wt_string. Args: + ---- wt_string(str): string for weathertype selection wt_cubes(iris.cube.CubeList): list of weathertype cubes - Raises: + Raises + ------ NameError: if wt_array does not exist for the given wt_string Returns @@ -701,7 +704,7 @@ def calc_wt_anomalies( """Calculate anomalies for psl, tas and pr for weathertypes. Args: - ------- + ---- cfg (dict): Configuration dictionary from recipe cube (iris.cube.Cube): Cube with variable data wt_cubes (iris.cube.CubeList): List of cubes of lwt, slwt_ERA5 and slwt_EOBS @@ -774,7 +777,7 @@ def calc_wt_std( """Calculate standard deviation for psl, tas and pr for weathertypes. Args: - ------- + ---- cfg (dict): Configuration dictionary from recipe cube (iris.cube.Cube): Cube with variable data wt_cubes (iris.cube.CubeList): List of cubes of lwt, slwt_ERA5 and slwt_EOBS @@ -839,7 +842,7 @@ def calc_lwt_model(cfg: dict, cube: iris.cube.Cube, data_info: dict): """Calculate Lamb weathertypes for models. Args: - ------- + ---- cfg (dict): Configuration dictionary from recipe cube (iris.cube.Cube): Cube with variable data data_info (dict): Dictionary with info to dataset @@ -906,10 +909,10 @@ def plot_means( data_info: dict, only_lwt=False, ): - """Function for plotting means, anomalies and standard deviations. + """Plotting means, anomalies and standard deviations. Args: - ------- + ---- cfg (dict): Configuration dictionary from recipe preproc_var (np.array): Preprocessed variable cube wt_cubes (iris.cube.Cube): List of cubes of lwt, slwt_ERA5 and slwt_EOBS diff --git a/esmvaltool/diag_scripts/weathertyping/plot_utils.py b/esmvaltool/diag_scripts/weathertyping/plot_utils.py index 4ddc2aa8cb..dfd1d6676f 100644 --- a/esmvaltool/diag_scripts/weathertyping/plot_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/plot_utils.py @@ -37,9 +37,11 @@ def generate_grayscale_hex_values(x): """Generate grayscale values for plotting seasonal occurences. Args: - x (list): Amount of weathertypes. + ---- + x (int): Amount of weathertypes. - Returns: + Returns + ------- list: List of grayscale hex values """ grayscale_values = np.linspace(0, 1, x) @@ -57,7 +59,7 @@ def plot_seasonal_occurrence( """Plot seasonal occurences of weathertypes. Args: - ------- + ---- cfg (dict): Configuration dictionary from recipe wt_cubes (iris.cube.Cube): List of cubes of lwt, slwt_ERA5 and slwt_EOBS data_info (dict): Dictionary with info to dataset @@ -155,10 +157,10 @@ def plot_seasonal_occurrence( def plot_maps( wt: np.array, cfg: dict, cube: iris.cube.Cube, data_info: dict, mode: str ): - """Function to plot maps of means, std and anomalies. + """Plotting maps of means, std and anomalies. Args: - ------- + ---- wt (np.array): WT array cfg (dict): Configuration dictionary from recipe cube (iris.cube.Cube): Data to be plotted @@ -268,7 +270,7 @@ def plot_corr_rmse_heatmaps( """Plot correlation and rmse heatmaps. Args: - ------- + ---- cfg (dict): Configuration dictionary from recipe pattern_correlation_matrix (np.array): Pattern correlation matrix rmse_matrix (np.array): RMSE matrix @@ -344,7 +346,7 @@ def plot_corr_rmse_heatmaps( def get_colormap(colormap_string: str) -> ListedColormap: - """Get colormaps for plottings + """Get colormaps for plottings. Args: colormap_string (str): string to identify colormap diff --git a/esmvaltool/diag_scripts/weathertyping/weathertyping.py b/esmvaltool/diag_scripts/weathertyping/weathertyping.py index 49f553afaa..e3c2c04c6a 100644 --- a/esmvaltool/diag_scripts/weathertyping/weathertyping.py +++ b/esmvaltool/diag_scripts/weathertyping/weathertyping.py @@ -40,7 +40,7 @@ def process_models_automatic_slwt( """Process model data for calculating Lamb and simplified weathertypes. Args: - ------- + ---- cfg (dict): Nested dictionary of metadata dataset_vars (list): List of variable dictionaries for a specific dataset data_info (dict): Dictionary holding dataset information. @@ -67,23 +67,22 @@ def process_models_automatic_slwt( cfg, wt_preproc, data_info, cfg.get("predefined_slwt") ) - # load wt files - wt_cubes = load_wt_files( - f"{cfg.get('work_dir')}/{output_file_path}" - f"/{data_info['dataset']}" - f"{data_info['driver']}_" - f"{data_info['ensemble']}_" - f"{data_info['timerange']}.nc" - ) - - var_dict = { - f"{ensemble_var.get('short_name')}": get_preproc_lists_ensemble( - ensemble_var - ) - } - # plot means if cfg.get("plotting", False): + # load wt files + wt_cubes = load_wt_files( + f"{cfg.get('work_dir')}/{output_file_path}" + f"/{data_info['dataset']}" + f"{data_info['driver']}_" + f"{data_info['ensemble']}_" + f"{data_info['timerange']}.nc" + ) + + var_dict = { + f"{ensemble_var.get('short_name')}": get_preproc_lists_ensemble( + ensemble_var + ) + } for var_name, var_data in var_dict.items(): data_info["var"] = var_name data_info["preproc_path"] = var_data[1] @@ -101,7 +100,7 @@ def process_era5_automatic_slwt( """Process ERA5 data for calculating Lamb and simplified weathertypes. Args: - ------- + ---- data_info (dict): Dictionary holding dataset information. preproc_variables_dict (dict): Dictionary holding preprocessed variables for all datasets. cfg (dict): Nested dictionary of metadata @@ -154,10 +153,10 @@ def process_era5_automatic_slwt( f"{cfg.get('work_dir')}/{data_info['dataset']}.nc" ) - var_dict = get_looping_dict( - dataset_vars - ) # dataset_vars is list of variables for dataset dataset_name if cfg.get("plotting", False): + var_dict = get_looping_dict( + dataset_vars + ) # dataset_vars is list of variables for dataset dataset_name # plot means for var_name, var_data in var_dict.items(): data_info["var"] = var_name @@ -235,11 +234,10 @@ def process_era5_lwt(preproc_variables_dict, cfg, dataset_vars, data_info): f"{cfg.get('work_dir')}/{data_info['dataset']}.nc", only_lwt=True ) - var_dict = get_looping_dict( - dataset_vars - ) # dataset_vars is list of variables for dataset dataset_name - if cfg.get("plotting", False): + var_dict = get_looping_dict( + dataset_vars + ) # dataset_vars is list of variables for dataset dataset_name # plot means for var_name, var_data in var_dict.items(): data_info["var"] = var_name @@ -347,6 +345,8 @@ def run_my_diagnostic(cfg: dict): automatic_slwt = cfg.get("automatic_slwt") # check if user wants to calculate simplified weathertypes automatically + # for that, automatic_slwt must be true + # additionally, if a predefined_slwt is given, those will be used if automatic_slwt: run_automatic_slwt(cfg) # if automatic_slwt is false, and predefined_slwt is false, diff --git a/esmvaltool/diag_scripts/weathertyping/wt_utils.py b/esmvaltool/diag_scripts/weathertyping/wt_utils.py index 92e3b0a67a..8f76ea9d01 100644 --- a/esmvaltool/diag_scripts/weathertyping/wt_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/wt_utils.py @@ -36,8 +36,6 @@ def get_driver(data_info: dict) -> str: str: Driver string with leading underscore or empty string. """ driver = data_info.get("driver", "") - if driver != "": - driver = f"_{driver}" return driver @@ -55,12 +53,19 @@ def load_wt_preprocessors(dataset: str, preproc_variables_dict: dict): wt_preproc = iris.load_cube( preproc_variables_dict.get(dataset)[0].get("filename") ) - wt_preproc_prcp = iris.load_cube( - preproc_variables_dict.get(dataset)[1].get("filename") - ) - wt_preproc_prcp_eobs = iris.load_cube( - preproc_variables_dict.get("E-OBS")[0].get("filename") - ) + # try and except block to catch missing precipitation preprocessor + # if it is not supplied, predefined_slwt has to be given by user + try: + wt_preproc_prcp = iris.load_cube( + preproc_variables_dict.get(dataset)[1].get("filename") + ) + wt_preproc_prcp_eobs = iris.load_cube( + preproc_variables_dict.get("E-OBS")[0].get("filename") + ) + except (IndexError, KeyError, TypeError): + print("ERA5 precipitation preprocessor not found for automatic slwt.") + wt_preproc_prcp = None + wt_preproc_prcp_eobs = None return wt_preproc, wt_preproc_prcp, wt_preproc_prcp_eobs @@ -78,12 +83,19 @@ def get_ancestors_era5_eobs(dataset: str, preproc_variables_dict: dict): """ era5_ancestors = [ preproc_variables_dict.get(dataset)[0].get("filename"), - preproc_variables_dict.get(dataset)[1].get("filename"), - ] - eobs_ancestors = [ - preproc_variables_dict.get(dataset)[0].get("filename"), - preproc_variables_dict.get("E-OBS")[0].get("filename"), ] + try: + era5_ancestors.append( + preproc_variables_dict.get(dataset)[1].get("filename") + ) + eobs_ancestors = [ + preproc_variables_dict.get(dataset)[0].get("filename"), + preproc_variables_dict.get("E-OBS")[0].get("filename"), + ] + except (IndexError, KeyError, TypeError): + print("No ancestors for ERA5 and E-OBS precipitation preprocessor.") + eobs_ancestors = [] + return era5_ancestors, eobs_ancestors @@ -235,7 +247,7 @@ def log_provenance(filename: str, cfg: dict, provenance_record: dict): """Log provenance. Args: - ------- + ---- filename (str): Filename of xml file cfg (dict): Configuration dictionary provenance_record (dict): Provenance record dictionary @@ -303,7 +315,7 @@ def write_mapping_dict(work_dir: str, dataset: str, mapping_dict: dict): """Write mapping dictionary to file. Args: - ------- + ---- work_dir (str): Current working directory dataset (str): Dataset name mapping_dict (dict): Mapping dictionary in {lwt: slwt, ...} format @@ -319,8 +331,8 @@ def write_mapping_dict(work_dir: str, dataset: str, mapping_dict: dict): def convert_dict(dict_: dict) -> dict: """Convert dictionary from {lwt: slwt, ...} format to {slwt: [lwt1, lwt2], ...}. - Args: + ---- dict_ (dict): Mapping dictionary to be converted Returns @@ -363,7 +375,7 @@ def write_corr_rmse_to_csv( """Write correlation and rsme matrix to csv files. Args: - ------- + ---- cfg (dict): Configuration dictionary from recipe pattern_correlation_matrix (np.array): Correlation matrix rmse_matrix (np.array): RSME matrix @@ -394,6 +406,7 @@ def run_predefined_slwt( """Run predefined slwt mapping. Args: + ---- work_dir (str): Working directory to save mapping dict dataset_name (str): Name of dataset lwt (np.array): lwt array @@ -419,7 +432,7 @@ def combine_wt_to_file( """Combine lwt and slwt arrays to one file. Args: - ------- + ---- cfg (dict): Configuration dictionary from recipe wt_list (list): List of weathertype arrays cube (iris.cube.Cube): Cube of data to keep time coordinate @@ -464,7 +477,7 @@ def write_lwt_to_file( """Write only lwt to file. Args: - ------- + ---- cfg (dict): Configuration dictionary from recipe lwt (np.array): lwt array cube (iris.cube.Cube): Cube of data to keep time coordinate @@ -493,11 +506,12 @@ def map_lwt_to_slwt(lwt: np.array, mapping_dict: dict) -> np.array: """Map lwt array to slwt array. Args: + ---- lwt (np.array): lwt array mapping_dict (dict): Mapping dictionary in {lwt: slwt, ...} format Returns - -------- + ------- np.array: array of slwt """ return np.array([np.int8(mapping_dict.get(value, 0)) for value in lwt]) @@ -507,10 +521,11 @@ def check_mapping_dict_format(mapping_dict: dict) -> dict: """Check format of mapping dict and return in {lwt: slwt, ...} format. Args: + ---- mapping_dict (dict): mapping dict in any format Returns - -------- + ------- dict: mapping dict in {lwt: slwt, ...} format """ if isinstance(mapping_dict.get(list(mapping_dict.keys())[0]), list): From b6ac979cae4118df34b2b1458461208e0f2bafcf Mon Sep 17 00:00:00 2001 From: Thomas Kroissenbrunner Date: Fri, 28 Nov 2025 14:30:35 +0100 Subject: [PATCH 13/57] update --- .../diag_scripts/weathertyping/calc_utils.py | 18 ++++++++++++------ .../diag_scripts/weathertyping/plot_utils.py | 10 ++++++---- .../weathertyping/weathertyping.py | 6 ++++-- .../diag_scripts/weathertyping/wt_utils.py | 7 +++---- 4 files changed, 25 insertions(+), 16 deletions(-) diff --git a/esmvaltool/diag_scripts/weathertyping/calc_utils.py b/esmvaltool/diag_scripts/weathertyping/calc_utils.py index 663ddfda9e..d82f0528df 100644 --- a/esmvaltool/diag_scripts/weathertyping/calc_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/calc_utils.py @@ -102,7 +102,11 @@ def calc_slwt_obs( write_mapping_dict(cfg.get("work_dir"), dataset, mapping_dict) provenance_record = get_provenance_record( - "Lamb Weathertypes", ancestors, ["Lamb Weathertypes"], False, False + "Lamb Weathertypes", + ancestors, + ["Lamb Weathertypes"], + plot_types=False, + statistics=False, ) log_provenance( @@ -230,7 +234,8 @@ def calc_westerly_shear_velocity( def calc_southerly_shear_velocity( - cube: iris.cube.Cube, const4: float + cube: iris.cube.Cube, + const4: float, ) -> np.array: """Calculate southerly shear velocity. @@ -264,7 +269,8 @@ def calc_southerly_shear_velocity( def calc_total_shear_velocity( - westerly_shear_velocity: np.array, southerly_shear_velocity: np.array + westerly_shear_velocity: np.array, + southerly_shear_velocity: np.array, ) -> np.array: """Calculate total shear velocity. @@ -357,7 +363,7 @@ def wt_algorithm(cube: iris.cube.Cube, dataset: str) -> np.array: # Lamb pure directional type if abs(z_i) < total_flow[i]: - if 337.5 <= direction or direction < 22.5: + if direction >= 337.5 or direction < 22.5: weathertypes[i] = 1 elif 22.5 <= direction < 67.5: weathertypes[i] = 2 @@ -383,7 +389,7 @@ def wt_algorithm(cube: iris.cube.Cube, dataset: str) -> np.array: # Lambs’s synoptic/direction hybrid types elif total_flow[i] < abs(z_i) < (2 * total_flow[i]): if z_i > 0: - if 337.5 <= direction or direction < 22.5: + if direction >= 337.5 or direction < 22.5: weathertypes[i] = 11 elif 22.5 <= direction < 67.5: weathertypes[i] = 12 @@ -401,7 +407,7 @@ def wt_algorithm(cube: iris.cube.Cube, dataset: str) -> np.array: weathertypes[i] = 18 elif z_i < 0: - if 337.5 <= direction or direction < 22.5: + if direction >= 337.5 or direction < 22.5: weathertypes[i] = 19 elif 22.5 <= direction < 67.5: weathertypes[i] = 20 diff --git a/esmvaltool/diag_scripts/weathertyping/plot_utils.py b/esmvaltool/diag_scripts/weathertyping/plot_utils.py index dfd1d6676f..761b8c4c7d 100644 --- a/esmvaltool/diag_scripts/weathertyping/plot_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/plot_utils.py @@ -54,7 +54,9 @@ def generate_grayscale_hex_values(x): def plot_seasonal_occurrence( - cfg: dict, wt_cubes: iris.cube.Cube, data_info: dict + cfg: dict, + wt_cubes: iris.cube.Cube, + data_info: dict, ): """Plot seasonal occurences of weathertypes. @@ -97,7 +99,7 @@ def plot_seasonal_occurrence( ).data unique, counts = np.unique(array, return_counts=True) month_dict[month] = dict( - zip(unique, counts / sum(counts), strict=False) + zip(unique, counts / sum(counts), strict=False), ) relative_occurrences[cube.long_name] = month_dict @@ -145,11 +147,11 @@ def plot_seasonal_occurrence( plt.savefig( f"{output_path}/{driver}{data_info.get('dataset')}_{data_info.get('ensemble', '')}_" - f"{wt_string}_rel_occurrence_{data_info.get('timerange')}.png" + f"{wt_string}_rel_occurrence_{data_info.get('timerange')}.png", ) plt.savefig( f"{output_path}/{driver}{data_info.get('dataset')}_{data_info.get('ensemble', '')}_" - f"{wt_string}_rel_occurrence_{data_info.get('timerange')}.pdf" + f"{wt_string}_rel_occurrence_{data_info.get('timerange')}.pdf", ) plt.close() diff --git a/esmvaltool/diag_scripts/weathertyping/weathertyping.py b/esmvaltool/diag_scripts/weathertyping/weathertyping.py index e3c2c04c6a..c0c5807da5 100644 --- a/esmvaltool/diag_scripts/weathertyping/weathertyping.py +++ b/esmvaltool/diag_scripts/weathertyping/weathertyping.py @@ -231,7 +231,8 @@ def process_era5_lwt(preproc_variables_dict, cfg, dataset_vars, data_info): # load wt files wt_cubes = load_wt_files( - f"{cfg.get('work_dir')}/{data_info['dataset']}.nc", only_lwt=True + f"{cfg.get('work_dir')}/{data_info['dataset']}.nc", + only_lwt=True, ) if cfg.get("plotting", False): @@ -267,7 +268,8 @@ def process_models_lwt(cfg: dict, dataset_vars: list, data_info: dict): wt_preproc = iris.load_cube(dataset_vars[0].get("filename")) output_file_path, preproc_path = get_model_output_filepath( - data_info["dataset"], dataset_vars + data_info["dataset"], + dataset_vars, ) data_info["output_file_path"] = output_file_path diff --git a/esmvaltool/diag_scripts/weathertyping/wt_utils.py b/esmvaltool/diag_scripts/weathertyping/wt_utils.py index 8f76ea9d01..230d9d330a 100644 --- a/esmvaltool/diag_scripts/weathertyping/wt_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/wt_utils.py @@ -3,8 +3,8 @@ # operating system manipulations (e.g. path constructions) import json import logging -import os import warnings +from pathlib import Path # to manipulate iris cubes import iris @@ -19,7 +19,7 @@ iris.FUTURE.datum_support = True -logger = logging.getLogger(os.path.basename(__file__)) +logger = logging.getLogger(Path(__file__).name) # Ignoring a warning that is produced when selecting timesteps of a weathertype warnings.filterwarnings("ignore", ".*Collapsing a non-contiguous coordinate*") @@ -306,9 +306,8 @@ def find_intersection(m_list: list) -> list: return m_list merged_tuples = find_intersection(s) - mapping_dict = turn_list_to_mapping_dict(merged_tuples) - return mapping_dict + return turn_list_to_mapping_dict(merged_tuples) def write_mapping_dict(work_dir: str, dataset: str, mapping_dict: dict): From 236cd79e275fe51ec5471a24222d2d8335e43c3d Mon Sep 17 00:00:00 2001 From: Thomas Kroissenbrunner Date: Fri, 28 Nov 2025 14:42:47 +0100 Subject: [PATCH 14/57] minor codacy issues --- .../diag_scripts/weathertyping/calc_utils.py | 15 +++-- .../diag_scripts/weathertyping/plot_utils.py | 57 +++++++++++-------- .../weathertyping/weathertyping.py | 49 ++++++++++------ .../diag_scripts/weathertyping/wt_utils.py | 48 +++++++++------- 4 files changed, 104 insertions(+), 65 deletions(-) diff --git a/esmvaltool/diag_scripts/weathertyping/calc_utils.py b/esmvaltool/diag_scripts/weathertyping/calc_utils.py index d82f0528df..51caa1adc8 100644 --- a/esmvaltool/diag_scripts/weathertyping/calc_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/calc_utils.py @@ -571,7 +571,7 @@ def process_prcp_mean( rmse_matrix[i][j] = rmse(data[i], data[j]) rmse_matrix[j][i] = rmse_matrix[i][j] if pattern_correlation_matrix[i][j] >= cfg.get( - "correlation_threshold" + "correlation_threshold", ) and rmse_matrix[i][j] <= cfg.get("rmse_threshold"): selected_pairs.append( ( @@ -583,11 +583,18 @@ def process_prcp_mean( # write matrices to csv write_corr_rmse_to_csv( - cfg, pattern_correlation_matrix, rmse_matrix, dataset + cfg, + pattern_correlation_matrix, + rmse_matrix, + dataset, ) # plot heatmaps for matrices plot_corr_rmse_heatmaps( - cfg, pattern_correlation_matrix, rmse_matrix, dataset, timerange + cfg, + pattern_correlation_matrix, + rmse_matrix, + dataset, + timerange, ) return selected_pairs @@ -695,7 +702,7 @@ def get_wt_array(wt_string: str, wt_cubes: iris.cube.CubeList) -> np.array: wt_array = lwt_cube.data[:] else: raise NameError( - "wt_array does not exist for calc_wt_means, calc_wt_anomalies or calc_wt_std." + "wt_array does not exist for calc_wt_means, calc_wt_anomalies or calc_wt_std.", ) return wt_array, tcoord diff --git a/esmvaltool/diag_scripts/weathertyping/plot_utils.py b/esmvaltool/diag_scripts/weathertyping/plot_utils.py index 761b8c4c7d..a2f51e274c 100644 --- a/esmvaltool/diag_scripts/weathertyping/plot_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/plot_utils.py @@ -2,8 +2,8 @@ # operating system manipulations (e.g. path constructions) import logging -import os import warnings +from pathlib import Path # plotting imports import cartopy.crs as ccrs @@ -27,7 +27,7 @@ iris.FUTURE.datum_support = True -logger = logging.getLogger(os.path.basename(__file__)) +logger = logging.getLogger(Path(__file__).name) # Ignoring a warning that is produced when selecting timesteps of a weathertype warnings.filterwarnings("ignore", ".*Collapsing a non-contiguous coordinate*") @@ -45,13 +45,12 @@ def generate_grayscale_hex_values(x): list: List of grayscale hex values """ grayscale_values = np.linspace(0, 1, x) - grayscale_hex = [ + + return [ f"#{int(value * 255):02x}{int(value * 255):02x}{int(value * 255):02x}" for value in grayscale_values ] - return grayscale_hex - def plot_seasonal_occurrence( cfg: dict, @@ -70,8 +69,8 @@ def plot_seasonal_occurrence( output_path = f"{cfg['plot_dir']}/seasonal_occurrence" - if not os.path.exists(f"{output_path}"): - os.makedirs(f"{output_path}") + if not Path.exists(f"{output_path}"): + Path.mkdir(f"{output_path}", parents=True, exist_ok=True) month_list = [ "Jan", @@ -95,7 +94,7 @@ def plot_seasonal_occurrence( month_dict = {} for month in range(1, 13): array = cube.extract( - iris.Constraint(time=iris.time.PartialDateTime(month=month)) + iris.Constraint(time=iris.time.PartialDateTime(month=month)), ).data unique, counts = np.unique(array, return_counts=True) month_dict[month] = dict( @@ -110,7 +109,7 @@ def plot_seasonal_occurrence( ) wt_stack = np.zeros((wt_numbers, 12)) for month, month_value in relative_occurrences.get(wt_string).items(): - for wt in month_value.keys(): + for wt in month_value: wt_stack[np.int8(wt - 1), month - 1] = month_value.get(wt) y = np.vstack(list(wt_stack)) @@ -120,11 +119,13 @@ def plot_seasonal_occurrence( ax_.set_title( f"Seasonal occurence of {wt_string}, \ - {data_info.get('dataset')}, {data_info.get('timerange')}" + {data_info.get('dataset')}, {data_info.get('timerange')}", ) ax_.stackplot( - month_list, y, colors=generate_grayscale_hex_values(wt_numbers) + month_list, + y, + colors=generate_grayscale_hex_values(wt_numbers), ) ax_.legend( @@ -133,7 +134,7 @@ def plot_seasonal_occurrence( fancybox=True, shadow=True, ncol=9, - labels=tuple(f"WT {i + 1}" for i in range(0, wt_numbers)), + labels=tuple(f"WT {i + 1}" for i in range(wt_numbers)), ) ax_.set( @@ -157,7 +158,11 @@ def plot_seasonal_occurrence( def plot_maps( - wt: np.array, cfg: dict, cube: iris.cube.Cube, data_info: dict, mode: str + wt: np.array, + cfg: dict, + cube: iris.cube.Cube, + data_info: dict, + mode: str, ): """Plotting maps of means, std and anomalies. @@ -186,7 +191,7 @@ def plot_maps( psl_cmap = get_colormap("psl") plt.title( f"{data_info.get('dataset')} {data_info.get('ensemble', '')}, {data_info.get('var')} {mode}\n" - + f"{data_info.get('timerange')}, wt: {wt}" + + f"{data_info.get('timerange')}, wt: {wt}", ) unit = "[hPa]" im = iplt.contourf(cube / 100, cmap=psl_cmap) @@ -199,13 +204,13 @@ def plot_maps( unit = "[m]" plt.title( f"{data_info.get('dataset')} {data_info.get('ensemble', '')}, total {var_name} {mode}\n" - + f"{data_info.get('timerange')}, wt: {wt}" + + f"{data_info.get('timerange')}, wt: {wt}", ) else: unit = "[kg m-2 s-1]" plt.title( f"{data_info.get('dataset')} {data_info.get('ensemble', '')}, {var_name} flux {mode}\n" - + f"{data_info.get('timerange')}, wt: {wt}" + + f"{data_info.get('timerange')}, wt: {wt}", ) im = iplt.contourf(cube, cmap=prcp_cmap) cb = plt.colorbar(im) @@ -216,7 +221,7 @@ def plot_maps( unit = "[K]" plt.title( f"{data_info.get('dataset')} {data_info.get('ensemble', '')}, 1000 hPa {var_name} {mode}\n" - + f"{data_info.get('timerange')}, wt: {wt}" + + f"{data_info.get('timerange')}, wt: {wt}", ) im = iplt.contourf(cube, cmap=temp_cmap) cb = plt.colorbar(im) @@ -248,16 +253,18 @@ def plot_maps( ax.coastlines() ax.add_feature(cfeature.BORDERS, linestyle=":") - if not os.path.exists(f"{cfg.get('plot_dir')}/{mode}"): - os.makedirs(f"{cfg.get('plot_dir')}/{mode}") + if not Path(f"{cfg.get('plot_dir')}/{mode}").exists(): + Path.mkdir( + f"{cfg.get('plot_dir')}/{mode}", parents=True, exist_ok=True + ) plt.savefig( f"{cfg.get('plot_dir')}/{mode}/{data_info.get('wt_string')}_{wt}{get_driver(data_info)}_{data_info.get('dataset')}_{data_info.get('ensemble', '')}" - f"_{var_name}_{mode}_{data_info.get('timerange')}.png" + f"_{var_name}_{mode}_{data_info.get('timerange')}.png", ) plt.savefig( f"{cfg.get('plot_dir')}/{mode}/{data_info.get('wt_string')}_{wt}{get_driver(data_info)}_{data_info.get('dataset')}_{data_info.get('ensemble', '')}_" - f"{var_name}_{mode}_{data_info.get('timerange')}.pdf" + f"{var_name}_{mode}_{data_info.get('timerange')}.pdf", ) plt.close() @@ -281,8 +288,8 @@ def plot_corr_rmse_heatmaps( """ output_path = f"{cfg.get('plot_dir')}/heatmaps" - if not os.path.exists(f"{output_path}"): - os.makedirs(f"{output_path}") + if not Path(f"{output_path}").exists(): + Path.mkdir(f"{output_path}", parents=True, exist_ok=True) labels = np.arange(1, 28) @@ -313,10 +320,10 @@ def plot_corr_rmse_heatmaps( ax.set_ylabel("lwt", fontsize=8) plt.tight_layout() plt.savefig( - f"{output_path}/correlation_matrix_{dataset}_{timerange}.png" + f"{output_path}/correlation_matrix_{dataset}_{timerange}.png", ) plt.savefig( - f"{output_path}/correlation_matrix_{dataset}_{timerange}.pdf" + f"{output_path}/correlation_matrix_{dataset}_{timerange}.pdf", ) plt.close() diff --git a/esmvaltool/diag_scripts/weathertyping/weathertyping.py b/esmvaltool/diag_scripts/weathertyping/weathertyping.py index c0c5807da5..5512599acb 100644 --- a/esmvaltool/diag_scripts/weathertyping/weathertyping.py +++ b/esmvaltool/diag_scripts/weathertyping/weathertyping.py @@ -35,7 +35,9 @@ def process_models_automatic_slwt( - cfg: dict, dataset_vars: list, data_info: dict + cfg: dict, + dataset_vars: list, + data_info: dict, ): """Process model data for calculating Lamb and simplified weathertypes. @@ -56,7 +58,8 @@ def process_models_automatic_slwt( wt_preproc = iris.load_cube(ensemble_var.get("filename")) output_file_path, preproc_path = get_model_output_filepath( - data_info["dataset"], ensemble_var + data_info["dataset"], + ensemble_var, ) data_info["output_file_path"] = output_file_path @@ -64,7 +67,10 @@ def process_models_automatic_slwt( # calculate weathertypes calc_lwt_slwt_model( - cfg, wt_preproc, data_info, cfg.get("predefined_slwt") + cfg, + wt_preproc, + data_info, + cfg.get("predefined_slwt"), ) # plot means @@ -75,12 +81,12 @@ def process_models_automatic_slwt( f"/{data_info['dataset']}" f"{data_info['driver']}_" f"{data_info['ensemble']}_" - f"{data_info['timerange']}.nc" + f"{data_info['timerange']}.nc", ) var_dict = { f"{ensemble_var.get('short_name')}": get_preproc_lists_ensemble( - ensemble_var + ensemble_var, ) } for var_name, var_data in var_dict.items(): @@ -107,14 +113,16 @@ def process_era5_automatic_slwt( dataset_vars (list): List of variable dictionaries for a specific dataset """ wt_preproc, wt_preproc_prcp, wt_preproc_prcp_eobs = load_wt_preprocessors( - data_info["dataset"], preproc_variables_dict + data_info["dataset"], + preproc_variables_dict, ) # calculate lwt lwt = wt_algorithm(wt_preproc, data_info["dataset"]) era5_ancestors, eobs_ancestors = get_ancestors_era5_eobs( - data_info["dataset"], preproc_variables_dict + data_info["dataset"], + preproc_variables_dict, ) # calculate simplified lwt based on precipitation @@ -150,12 +158,12 @@ def process_era5_automatic_slwt( # load weathertype files as cubes wt_cubes = load_wt_files( - f"{cfg.get('work_dir')}/{data_info['dataset']}.nc" + f"{cfg.get('work_dir')}/{data_info['dataset']}.nc", ) if cfg.get("plotting", False): var_dict = get_looping_dict( - dataset_vars + dataset_vars, ) # dataset_vars is list of variables for dataset dataset_name # plot means for var_name, var_data in var_dict.items(): @@ -172,11 +180,13 @@ def run_automatic_slwt(cfg: dict): of the weathertypes. Args: - ------- + ---- cfg (dict): Nested dictionary of metadata """ + preproc_variables_dict = group_metadata( - cfg.get("input_data").values(), "dataset" + cfg.get("input_data").values(), + "dataset", ) for dataset_name, dataset_vars in preproc_variables_dict.items(): data_info = { @@ -185,7 +195,10 @@ def run_automatic_slwt(cfg: dict): } if dataset_name == "ERA5": process_era5_automatic_slwt( - data_info, preproc_variables_dict, cfg, dataset_vars + data_info, + preproc_variables_dict, + cfg, + dataset_vars, ) else: if data_info["dataset"] == "E-OBS": @@ -220,8 +233,8 @@ def process_era5_lwt(preproc_variables_dict, cfg, dataset_vars, data_info): "Lamb Weathertypes", ancestors, ["Lamb Weathertypes"], - False, - False, + plot_types=False, + statistics=False, ) log_provenance(f"{cfg.get('work_dir')}/lwt_era5", cfg, provenance_record) @@ -319,7 +332,8 @@ def run_lwt(cfg: dict): cfg (dict): Nested dictionary of metadata """ preproc_variables_dict = group_metadata( - cfg.get("input_data").values(), "dataset" + cfg.get("input_data").values(), + "dataset", ) for dataset_name, dataset_vars in preproc_variables_dict.items(): data_info = { @@ -329,7 +343,10 @@ def run_lwt(cfg: dict): if dataset_name == "ERA5": process_era5_lwt( - preproc_variables_dict, cfg, dataset_vars, data_info + preproc_variables_dict, + cfg, + dataset_vars, + data_info, ) else: if data_info["dataset"] == "E-OBS": diff --git a/esmvaltool/diag_scripts/weathertyping/wt_utils.py b/esmvaltool/diag_scripts/weathertyping/wt_utils.py index 230d9d330a..5ced586bb2 100644 --- a/esmvaltool/diag_scripts/weathertyping/wt_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/wt_utils.py @@ -51,16 +51,16 @@ def load_wt_preprocessors(dataset: str, preproc_variables_dict: dict): tuple: Preprocessor cubes for weathertyping """ wt_preproc = iris.load_cube( - preproc_variables_dict.get(dataset)[0].get("filename") + preproc_variables_dict.get(dataset)[0].get("filename"), ) # try and except block to catch missing precipitation preprocessor # if it is not supplied, predefined_slwt has to be given by user try: wt_preproc_prcp = iris.load_cube( - preproc_variables_dict.get(dataset)[1].get("filename") + preproc_variables_dict.get(dataset)[1].get("filename"), ) wt_preproc_prcp_eobs = iris.load_cube( - preproc_variables_dict.get("E-OBS")[0].get("filename") + preproc_variables_dict.get("E-OBS")[0].get("filename"), ) except (IndexError, KeyError, TypeError): print("ERA5 precipitation preprocessor not found for automatic slwt.") @@ -86,7 +86,7 @@ def get_ancestors_era5_eobs(dataset: str, preproc_variables_dict: dict): ] try: era5_ancestors.append( - preproc_variables_dict.get(dataset)[1].get("filename") + preproc_variables_dict.get(dataset)[1].get("filename"), ) eobs_ancestors = [ preproc_variables_dict.get(dataset)[0].get("filename"), @@ -159,9 +159,6 @@ def get_preproc_lists_ensemble(preproc_vars: list): preproc = iris.load_cube(preproc_vars.get("filename")) - # preproc_list = [mean_preproc] - # preproc_path_list = [preproc_path_psl] - return preproc, preproc_path @@ -176,12 +173,12 @@ def get_looping_dict(preproc_vars: list): dict: Dictionary of preprocessor cubes and paths. """ preproc, preproc_path = get_preproc_lists(preproc_vars) - dict_ = { + + return { "psl": [preproc[0], preproc_path[0]], "pr": [preproc[1], preproc_path[1]], "tas": [preproc[2], preproc_path[2]], } - return dict_ def load_wt_files(path: str, only_lwt=False): @@ -293,7 +290,7 @@ def get_mapping_dict(selected_pairs: list) -> dict: mapping_array = [] for elem in selected_pairs: - mapping_array.append(elem[0]) + (mapping_array.append(elem[0]),) s = [set(i) for i in mapping_array if i] @@ -322,7 +319,9 @@ def write_mapping_dict(work_dir: str, dataset: str, mapping_dict: dict): mapping_dict_reformat = convert_dict(mapping_dict) with open( - f"{work_dir}/wt_mapping_dict_{dataset}.json", "w", encoding="utf-8" + f"{work_dir}/wt_mapping_dict_{dataset}.json", + "w", + encoding="utf-8", ) as file: json.dump(mapping_dict_reformat, file) @@ -388,19 +387,24 @@ def write_corr_rmse_to_csv( df_corr.index = range(1, len(df_corr) + 1) df_corr.columns = range(1, len(df_corr.columns) + 1) df_corr.to_csv( - f"{work_dir}/correlation_matrix_{dataset}.csv", index_label="Index" + f"{work_dir}/correlation_matrix_{dataset}.csv", + index_label="Index", ) df_rmse = pd.DataFrame(rmse_matrix) df_rmse.index = range(1, len(df_rmse) + 1) df_rmse.columns = range(1, len(df_rmse.columns) + 1) df_rmse.to_csv( - f"{work_dir}/rmse_matrix_{dataset}.csv", index_label="Index" + f"{work_dir}/rmse_matrix_{dataset}.csv", + index_label="Index", ) def run_predefined_slwt( - work_dir: str, dataset_name: str, lwt: np.array, predefined_slwt: dict + work_dir: str, + dataset_name: str, + lwt: np.array, + predefined_slwt: dict, ): """Run predefined slwt mapping. @@ -426,7 +430,10 @@ def run_predefined_slwt( def combine_wt_to_file( - cfg: dict, wt_list: list, cube: iris.cube.Cube, file_name: str + cfg: dict, + wt_list: list, + cube: iris.cube.Cube, + file_name: str, ): """Combine lwt and slwt arrays to one file. @@ -471,7 +478,10 @@ def combine_wt_to_file( def write_lwt_to_file( - cfg: dict, lwt: np.array, cube: iris.cube.Cube, file_name: str + cfg: dict, + lwt: np.array, + cube: iris.cube.Cube, + file_name: str, ): """Write only lwt to file. @@ -528,8 +538,6 @@ def check_mapping_dict_format(mapping_dict: dict) -> dict: dict: mapping dict in {lwt: slwt, ...} format """ if isinstance(mapping_dict.get(list(mapping_dict.keys())[0]), list): - dict_ = reverse_convert_dict(mapping_dict) + return reverse_convert_dict(mapping_dict) else: - dict_ = mapping_dict - - return dict_ + return mapping_dict From 793c016e99e0de97e90ba956a3853c9a3313c810 Mon Sep 17 00:00:00 2001 From: Thomas Kroissenbrunner Date: Fri, 28 Nov 2025 14:53:22 +0100 Subject: [PATCH 15/57] minor codacy issues --- .../diag_scripts/weathertyping/calc_utils.py | 73 ++++++++++++------- .../weathertyping/weathertyping.py | 1 + 2 files changed, 47 insertions(+), 27 deletions(-) diff --git a/esmvaltool/diag_scripts/weathertyping/calc_utils.py b/esmvaltool/diag_scripts/weathertyping/calc_utils.py index 51caa1adc8..24cab66f8d 100644 --- a/esmvaltool/diag_scripts/weathertyping/calc_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/calc_utils.py @@ -3,8 +3,8 @@ # operating system manipulations (e.g. path constructions) import json import logging -import os import warnings +from pathlib import Path # to manipulate iris cubes import iris @@ -30,7 +30,7 @@ iris.FUTURE.datum_support = True -logger = logging.getLogger(os.path.basename(__file__)) +logger = logging.getLogger(Path(__file__).name) # Ignoring a warning that is produced when selecting timesteps of a weathertype warnings.filterwarnings("ignore", ".*Collapsing a non-contiguous coordinate*") @@ -47,6 +47,7 @@ def calc_slwt_obs( """Calculate simplified weathertypes for observational data. Args: + ---- cfg (dict): Configuration dictionary from recipe lwt (np.array): Array of Lamb WT cube (iris.cube.Cube): Cube of psl data @@ -57,6 +58,7 @@ def calc_slwt_obs( Returns ------- np.array: Simplified Lamb Weathertypes + """ logger.info("Calculating simplified Lamb Weathertypes for %s", dataset) @@ -79,7 +81,7 @@ def calc_slwt_obs( extracted_cube = cube[target_indices] else: extracted_cube = cube.extract( - iris.Constraint(time=lambda t, d=dates: t.point in d[0]) + iris.Constraint(time=lambda t, d=dates: t.point in d[0]), ) wt_cube_mean = extracted_cube.collapsed("time", iris.analysis.MEAN) wt_data_prcp.append(wt_cube_mean.data.compressed()) @@ -185,7 +187,8 @@ def calc_southerly_flow(cube: iris.cube.Cube, const1: float) -> np.array: def calc_resultant_flow( - westerly_flow: np.array, southerly_flow: np.array + westerly_flow: np.array, + southerly_flow: np.array, ) -> np.array: """Calculate the resultant flow. @@ -206,7 +209,9 @@ def calc_resultant_flow( def calc_westerly_shear_velocity( - cube: iris.cube.Cube, const2: float, const3: float + cube: iris.cube.Cube, + const2: float, + const3: float, ) -> np.array: """Calculate westerly shear velocity. @@ -340,13 +345,16 @@ def wt_algorithm(cube: iris.cube.Cube, dataset: str) -> np.array: total_flow = calc_resultant_flow(westerly_flow, southerly_flow) # westerly shear vorticity westerly_shear_velocity = calc_westerly_shear_velocity( - cube, const2, const3 + cube, + const2, + const3, ) # southerly shear vorticity southerly_shear_velocity = calc_southerly_shear_velocity(cube, const4) # total shear vorticity total_shear_velocity = calc_total_shear_velocity( - westerly_shear_velocity, southerly_shear_velocity + westerly_shear_velocity, + southerly_shear_velocity, ) weathertypes = np.zeros(len(total_shear_velocity)) @@ -379,14 +387,14 @@ def wt_algorithm(cube: iris.cube.Cube, dataset: str) -> np.array: weathertypes[i] = 7 elif 292.5 <= direction < 337.5: weathertypes[i] = 8 - # Lamb’s pure cyclonic and anticyclonic type + # Lamb pure cyclonic and anticyclonic type elif (2 * total_flow[i]) < abs(z_i): if z_i > 0: weathertypes[i] = 9 elif z_i < 0: weathertypes[i] = 10 - # Lambs’s synoptic/direction hybrid types + # Lamb synoptic/direction hybrid types elif total_flow[i] < abs(z_i) < (2 * total_flow[i]): if z_i > 0: if direction >= 337.5 or direction < 22.5: @@ -423,7 +431,7 @@ def wt_algorithm(cube: iris.cube.Cube, dataset: str) -> np.array: weathertypes[i] = 25 elif 292.5 <= direction < 337.5: weathertypes[i] = 26 - # light indeterminate flow, corresponding to Lamb’s unclassified type U + # light indeterminate flow, corresponding to Lamb unclassified type U elif abs(z_i) < 6 and total_flow[i] < 6: weathertypes[i] = 27 @@ -449,11 +457,13 @@ def calc_lwt_slwt_model( """ driver = get_driver(data_info) - if not os.path.exists( - f"{cfg.get('work_dir')}/{data_info.get('output_file_path')}" - ): - os.makedirs( - f"{cfg.get('work_dir')}/{data_info.get('output_file_path')}" + if not Path( + f"{cfg.get('work_dir')}/{data_info.get('output_file_path')}", + ).exists(): + Path.mkdir( + f"{cfg.get('work_dir')}/{data_info.get('output_file_path')}", + parents=True, + exist_ok=True, ) lwt = wt_algorithm(cube, data_info.get("dataset")) @@ -525,7 +535,11 @@ def calc_lwt_slwt_model( f"{cfg.get('work_dir')}/wt_mapping_dict_E-OBS.json", ] provenance_record = get_provenance_record( - "Lamb Weathertypes", ancestors, ["Lamb Weathertypes"], False, False + "Lamb Weathertypes", + ancestors, + ["Lamb Weathertypes"], + plot_types=False, + statistics=False, ) log_provenance( @@ -643,7 +657,7 @@ def calc_wt_means( tcoord.units.num2date(tcoord.points[i]) for i in target_indices ] extracted_cube = cube.extract( - iris.Constraint(time=lambda t, d=dates: t.point in d[0]) + iris.Constraint(time=lambda t, d=dates: t.point in d[0]), ) wt_cube_mean = extracted_cube.collapsed("time", iris.analysis.MEAN) plot_maps(wt, cfg, wt_cube_mean, data_info, "mean") @@ -701,8 +715,9 @@ def get_wt_array(wt_string: str, wt_cubes: iris.cube.CubeList) -> np.array: tcoord = lwt_cube.coord("time") wt_array = lwt_cube.data[:] else: + error_str = "wt_array does not exist for calc_wt_means, calc_wt_anomalies or calc_wt_std." raise NameError( - "wt_array does not exist for calc_wt_means, calc_wt_anomalies or calc_wt_std.", + error_str, ) return wt_array, tcoord @@ -749,7 +764,7 @@ def calc_wt_anomalies( tcoord.units.num2date(tcoord.points[i]) for i in target_indices ] extracted_cube = cube.extract( - iris.Constraint(time=lambda t, d=dates: t.point in d[0]) + iris.Constraint(time=lambda t, d=dates: t.point in d[0]), ) wt_cube_mean = extracted_cube.collapsed("time", iris.analysis.MEAN) plot_maps( @@ -822,7 +837,7 @@ def calc_wt_std( tcoord.units.num2date(tcoord.points[i]) for i in target_indices ] extracted_cube = cube.extract( - iris.Constraint(time=lambda t, d=dates: t.point in d[0]) + iris.Constraint(time=lambda t, d=dates: t.point in d[0]), ) wt_cube_std = extracted_cube.collapsed("time", iris.analysis.STD_DEV) plot_maps(wt, cfg, wt_cube_std, data_info, "stddev") @@ -862,12 +877,12 @@ def calc_lwt_model(cfg: dict, cube: iris.cube.Cube, data_info: dict): """ driver = get_driver(data_info) - if not os.path.exists( - f"{cfg.get('work_dir')}/{data_info.get('output_file_path')}" - ): - os.makedirs( - f"{cfg.get('work_dir')}/{data_info.get('output_file_path')}" - ) + if not Path( + f"{cfg.get('work_dir')}/{data_info.get('output_file_path')}", + ).exists(): + Path( + f"{cfg.get('work_dir')}/{data_info.get('output_file_path')}", + ).mkdir(parents=True, exist_ok=True) wt = wt_algorithm(cube, data_info.get("dataset")) @@ -905,7 +920,11 @@ def calc_lwt_model(cfg: dict, cube: iris.cube.Cube, data_info: dict): f"{cfg.get('work_dir')}/wt_mapping_dict_E-OBS.json", ] provenance_record = get_provenance_record( - "Lamb Weathertypes", ancestors, ["Lamb Weathertypes"], False, False + "Lamb Weathertypes", + ancestors, + ["Lamb Weathertypes"], + plot_types=False, + statistics=False, ) log_provenance( diff --git a/esmvaltool/diag_scripts/weathertyping/weathertyping.py b/esmvaltool/diag_scripts/weathertyping/weathertyping.py index 5512599acb..5e08bdbe49 100644 --- a/esmvaltool/diag_scripts/weathertyping/weathertyping.py +++ b/esmvaltool/diag_scripts/weathertyping/weathertyping.py @@ -182,6 +182,7 @@ def run_automatic_slwt(cfg: dict): Args: ---- cfg (dict): Nested dictionary of metadata + """ preproc_variables_dict = group_metadata( From d1fb4f8a5dc6090438bc24221ca1622f75bf8da1 Mon Sep 17 00:00:00 2001 From: Thomas Kroissenbrunner Date: Fri, 28 Nov 2025 15:03:05 +0100 Subject: [PATCH 16/57] minor codacy issues --- .../diag_scripts/weathertyping/plot_utils.py | 14 ++++++++------ .../diag_scripts/weathertyping/weathertyping.py | 2 +- esmvaltool/diag_scripts/weathertyping/wt_utils.py | 6 ++---- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/esmvaltool/diag_scripts/weathertyping/plot_utils.py b/esmvaltool/diag_scripts/weathertyping/plot_utils.py index a2f51e274c..346f213378 100644 --- a/esmvaltool/diag_scripts/weathertyping/plot_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/plot_utils.py @@ -164,7 +164,7 @@ def plot_maps( data_info: dict, mode: str, ): - """Plotting maps of means, std and anomalies. + """Plot maps of means, std and anomalies. Args: ---- @@ -191,7 +191,7 @@ def plot_maps( psl_cmap = get_colormap("psl") plt.title( f"{data_info.get('dataset')} {data_info.get('ensemble', '')}, {data_info.get('var')} {mode}\n" - + f"{data_info.get('timerange')}, wt: {wt}", + f"{data_info.get('timerange')}, wt: {wt}", ) unit = "[hPa]" im = iplt.contourf(cube / 100, cmap=psl_cmap) @@ -204,13 +204,13 @@ def plot_maps( unit = "[m]" plt.title( f"{data_info.get('dataset')} {data_info.get('ensemble', '')}, total {var_name} {mode}\n" - + f"{data_info.get('timerange')}, wt: {wt}", + f"{data_info.get('timerange')}, wt: {wt}", ) else: unit = "[kg m-2 s-1]" plt.title( f"{data_info.get('dataset')} {data_info.get('ensemble', '')}, {var_name} flux {mode}\n" - + f"{data_info.get('timerange')}, wt: {wt}", + f"{data_info.get('timerange')}, wt: {wt}", ) im = iplt.contourf(cube, cmap=prcp_cmap) cb = plt.colorbar(im) @@ -221,7 +221,7 @@ def plot_maps( unit = "[K]" plt.title( f"{data_info.get('dataset')} {data_info.get('ensemble', '')}, 1000 hPa {var_name} {mode}\n" - + f"{data_info.get('timerange')}, wt: {wt}", + f"{data_info.get('timerange')}, wt: {wt}", ) im = iplt.contourf(cube, cmap=temp_cmap) cb = plt.colorbar(im) @@ -255,7 +255,9 @@ def plot_maps( if not Path(f"{cfg.get('plot_dir')}/{mode}").exists(): Path.mkdir( - f"{cfg.get('plot_dir')}/{mode}", parents=True, exist_ok=True + f"{cfg.get('plot_dir')}/{mode}", + parents=True, + exist_ok=True, ) plt.savefig( diff --git a/esmvaltool/diag_scripts/weathertyping/weathertyping.py b/esmvaltool/diag_scripts/weathertyping/weathertyping.py index 5e08bdbe49..5ceb819212 100644 --- a/esmvaltool/diag_scripts/weathertyping/weathertyping.py +++ b/esmvaltool/diag_scripts/weathertyping/weathertyping.py @@ -87,7 +87,7 @@ def process_models_automatic_slwt( var_dict = { f"{ensemble_var.get('short_name')}": get_preproc_lists_ensemble( ensemble_var, - ) + ), } for var_name, var_data in var_dict.items(): data_info["var"] = var_name diff --git a/esmvaltool/diag_scripts/weathertyping/wt_utils.py b/esmvaltool/diag_scripts/weathertyping/wt_utils.py index 5ced586bb2..1ad000d861 100644 --- a/esmvaltool/diag_scripts/weathertyping/wt_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/wt_utils.py @@ -35,8 +35,7 @@ def get_driver(data_info: dict) -> str: ------- str: Driver string with leading underscore or empty string. """ - driver = data_info.get("driver", "") - return driver + return data_info.get("driver", "") def load_wt_preprocessors(dataset: str, preproc_variables_dict: dict): @@ -539,5 +538,4 @@ def check_mapping_dict_format(mapping_dict: dict) -> dict: """ if isinstance(mapping_dict.get(list(mapping_dict.keys())[0]), list): return reverse_convert_dict(mapping_dict) - else: - return mapping_dict + return mapping_dict From 216ed8f2f0faee271deb0eae6c08fc19e5290506 Mon Sep 17 00:00:00 2001 From: Thomas Kroissenbrunner Date: Fri, 28 Nov 2025 15:11:50 +0100 Subject: [PATCH 17/57] minor codacy issues --- esmvaltool/diag_scripts/weathertyping/calc_utils.py | 12 ++++++------ esmvaltool/diag_scripts/weathertyping/plot_utils.py | 7 +++---- .../diag_scripts/weathertyping/weathertyping.py | 10 +++++----- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/esmvaltool/diag_scripts/weathertyping/calc_utils.py b/esmvaltool/diag_scripts/weathertyping/calc_utils.py index 24cab66f8d..a994cd06df 100644 --- a/esmvaltool/diag_scripts/weathertyping/calc_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/calc_utils.py @@ -304,6 +304,7 @@ def wt_algorithm(cube: iris.cube.Cube, dataset: str) -> np.array: Int. J. Climatol., 13: 655-663. https://doi.org/10.1002/joc.3370130606 Args: + ---- cube (iris.cube.Cube): Cube of psl data dataset (str): Name of dataset @@ -311,7 +312,6 @@ def wt_algorithm(cube: iris.cube.Cube, dataset: str) -> np.array: ------- np.array: Lamb weathertypes """ - # lats and lons corresponding to datapoints # 55, 5 -> 1 # 55, 15 -> 2 @@ -332,7 +332,6 @@ def wt_algorithm(cube: iris.cube.Cube, dataset: str) -> np.array: # lons: -5, 0, 5, 10, 15, 20, 25 # lats: 35, 40, 45, 50, 55 - logger.info("Calculating Lamb Weathertypes for %s", dataset) const1, const2, const3, const4 = calc_const() @@ -460,8 +459,9 @@ def calc_lwt_slwt_model( if not Path( f"{cfg.get('work_dir')}/{data_info.get('output_file_path')}", ).exists(): - Path.mkdir( - f"{cfg.get('work_dir')}/{data_info.get('output_file_path')}", + Path( + f"{cfg.get('work_dir')}/{data_info.get('output_file_path')}" + ).mkdir( parents=True, exist_ok=True, ) @@ -592,7 +592,7 @@ def process_prcp_mean( (i + 1, j + 1), pattern_correlation_matrix[i][j], rmse_matrix[i][j], - ) + ), ) # write matrices to csv @@ -941,7 +941,7 @@ def plot_means( data_info: dict, only_lwt=False, ): - """Plotting means, anomalies and standard deviations. + """Plot means, anomalies and standard deviations. Args: ---- diff --git a/esmvaltool/diag_scripts/weathertyping/plot_utils.py b/esmvaltool/diag_scripts/weathertyping/plot_utils.py index 346f213378..c2fadd2a61 100644 --- a/esmvaltool/diag_scripts/weathertyping/plot_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/plot_utils.py @@ -70,7 +70,7 @@ def plot_seasonal_occurrence( output_path = f"{cfg['plot_dir']}/seasonal_occurrence" if not Path.exists(f"{output_path}"): - Path.mkdir(f"{output_path}", parents=True, exist_ok=True) + Path(f"{output_path}").mkdir(parents=True, exist_ok=True) month_list = [ "Jan", @@ -254,8 +254,7 @@ def plot_maps( ax.add_feature(cfeature.BORDERS, linestyle=":") if not Path(f"{cfg.get('plot_dir')}/{mode}").exists(): - Path.mkdir( - f"{cfg.get('plot_dir')}/{mode}", + Path(f"{cfg.get('plot_dir')}/{mode}").mkdir( parents=True, exist_ok=True, ) @@ -291,7 +290,7 @@ def plot_corr_rmse_heatmaps( output_path = f"{cfg.get('plot_dir')}/heatmaps" if not Path(f"{output_path}").exists(): - Path.mkdir(f"{output_path}", parents=True, exist_ok=True) + Path(f"{output_path}").mkdir(parents=True, exist_ok=True) labels = np.arange(1, 28) diff --git a/esmvaltool/diag_scripts/weathertyping/weathertyping.py b/esmvaltool/diag_scripts/weathertyping/weathertyping.py index 5ceb819212..8524b19975 100644 --- a/esmvaltool/diag_scripts/weathertyping/weathertyping.py +++ b/esmvaltool/diag_scripts/weathertyping/weathertyping.py @@ -182,9 +182,7 @@ def run_automatic_slwt(cfg: dict): Args: ---- cfg (dict): Nested dictionary of metadata - """ - preproc_variables_dict = group_metadata( cfg.get("input_data").values(), "dataset", @@ -218,14 +216,16 @@ def process_era5_lwt(preproc_variables_dict, cfg, dataset_vars, data_info): data_info (dict): Dictionary holding dataset information. """ wt_preproc, _, _ = load_wt_preprocessors( - data_info["dataset"], preproc_variables_dict + data_info["dataset"], + preproc_variables_dict, ) # calculate lwt lwt = wt_algorithm(wt_preproc, data_info["dataset"]) era5_ancestors, eobs_ancestors = get_ancestors_era5_eobs( - data_info["dataset"], preproc_variables_dict + data_info["dataset"], + preproc_variables_dict, ) ancestors = [era5_ancestors, eobs_ancestors] @@ -251,7 +251,7 @@ def process_era5_lwt(preproc_variables_dict, cfg, dataset_vars, data_info): if cfg.get("plotting", False): var_dict = get_looping_dict( - dataset_vars + dataset_vars, ) # dataset_vars is list of variables for dataset dataset_name # plot means for var_name, var_data in var_dict.items(): From 04dd54f7de088fd042ff126fba01bb2a6025fd36 Mon Sep 17 00:00:00 2001 From: Thomas Kroissenbrunner Date: Mon, 1 Dec 2025 13:30:39 +0100 Subject: [PATCH 18/57] some medium issues --- .gitignore | 4 ++++ .../diag_scripts/weathertyping/__init__.py | 1 + .../diag_scripts/weathertyping/calc_utils.py | 10 ++------ .../weathertyping/weathertyping.py | 8 +++---- .../diag_scripts/weathertyping/wt_utils.py | 24 ++++++++++--------- 5 files changed, 23 insertions(+), 24 deletions(-) create mode 100644 esmvaltool/diag_scripts/weathertyping/__init__.py diff --git a/.gitignore b/.gitignore index 7c783dce5a..abec495ffb 100644 --- a/.gitignore +++ b/.gitignore @@ -112,3 +112,7 @@ esmvaltool/utils/testing/regression/.service/ # Temporary imagehash files imagehashes_*.yml + + +#Ignore vscode AI rules +.github/instructions/codacy.instructions.md diff --git a/esmvaltool/diag_scripts/weathertyping/__init__.py b/esmvaltool/diag_scripts/weathertyping/__init__.py new file mode 100644 index 0000000000..e31afa296a --- /dev/null +++ b/esmvaltool/diag_scripts/weathertyping/__init__.py @@ -0,0 +1 @@ +"""Initialize the ESMValTool weathertyping package.""" diff --git a/esmvaltool/diag_scripts/weathertyping/calc_utils.py b/esmvaltool/diag_scripts/weathertyping/calc_utils.py index a994cd06df..3221e8c12f 100644 --- a/esmvaltool/diag_scripts/weathertyping/calc_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/calc_utils.py @@ -107,8 +107,6 @@ def calc_slwt_obs( "Lamb Weathertypes", ancestors, ["Lamb Weathertypes"], - plot_types=False, - statistics=False, ) log_provenance( @@ -441,7 +439,7 @@ def calc_lwt_slwt_model( cfg: dict, cube: iris.cube.Cube, data_info: dict, - predefined_slwt: bool | dict, + predefined_slwt: dict = None, ): """Calculate Lamb as well as simplified weathertypes for model and write them to file. @@ -450,7 +448,7 @@ def calc_lwt_slwt_model( cfg (dict): Configuration dictionary from recipe cube (iris.cube.Cube): PSL field of dataset data_info (dict): Dictionary with info to dataset - predefined_slwt (bool | dict): If False, automatic_slwt will be used. + predefined_slwt (dict | None): If None, automatic_slwt will be used. If dict, this mapping dict will be used. (see recipe option predefined_slwt) """ @@ -538,8 +536,6 @@ def calc_lwt_slwt_model( "Lamb Weathertypes", ancestors, ["Lamb Weathertypes"], - plot_types=False, - statistics=False, ) log_provenance( @@ -923,8 +919,6 @@ def calc_lwt_model(cfg: dict, cube: iris.cube.Cube, data_info: dict): "Lamb Weathertypes", ancestors, ["Lamb Weathertypes"], - plot_types=False, - statistics=False, ) log_provenance( diff --git a/esmvaltool/diag_scripts/weathertyping/weathertyping.py b/esmvaltool/diag_scripts/weathertyping/weathertyping.py index 8524b19975..2ef83b3137 100644 --- a/esmvaltool/diag_scripts/weathertyping/weathertyping.py +++ b/esmvaltool/diag_scripts/weathertyping/weathertyping.py @@ -234,8 +234,6 @@ def process_era5_lwt(preproc_variables_dict, cfg, dataset_vars, data_info): "Lamb Weathertypes", ancestors, ["Lamb Weathertypes"], - plot_types=False, - statistics=False, ) log_provenance(f"{cfg.get('work_dir')}/lwt_era5", cfg, provenance_record) @@ -246,7 +244,7 @@ def process_era5_lwt(preproc_variables_dict, cfg, dataset_vars, data_info): # load wt files wt_cubes = load_wt_files( f"{cfg.get('work_dir')}/{data_info['dataset']}.nc", - only_lwt=True, + mode="lamb", ) if cfg.get("plotting", False): @@ -299,11 +297,11 @@ def process_models_lwt(cfg: dict, dataset_vars: list, data_info: dict): f"{data_info['driver']}" f"_{data_info['ensemble']}_" f"{data_info['timerange']}.nc", - only_lwt=True, + mode="lamb", ) var_dict = get_looping_dict( - dataset_vars + dataset_vars, ) # dataset_vars is list of variables for dataset_name if cfg.get("plotting", False): diff --git a/esmvaltool/diag_scripts/weathertyping/wt_utils.py b/esmvaltool/diag_scripts/weathertyping/wt_utils.py index 1ad000d861..eb4e982bbb 100644 --- a/esmvaltool/diag_scripts/weathertyping/wt_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/wt_utils.py @@ -180,25 +180,27 @@ def get_looping_dict(preproc_vars: list): } -def load_wt_files(path: str, only_lwt=False): +def load_wt_files(path: str, mode="simplified"): """Load wt files. Args: path (str): Path of wt file - only_lwt (bool, optional): If True, only Lamb weathertypes will be loaded. Defaults to False. + mode (str, optional): Type of weathertype to load. Defaults to "simplified". Returns ------- list: List of weathertype cubes. """ - if not only_lwt: + if mode == "simplified": lwt_cube = iris.load_cube(path, "lwt") slwt_era5_cube = iris.load_cube(path, "slwt_era5") slwt_eobs_cube = iris.load_cube(path, "slwt_eobs") wt_cubes = [lwt_cube, slwt_era5_cube, slwt_eobs_cube] - else: + elif mode == "lamb": lwt_cube = iris.load_cube(path, "lwt") wt_cubes = [lwt_cube] + else: + raise ValueError("Mode not recognized. Use 'simplified' or 'lamb'.") return wt_cubes @@ -207,8 +209,8 @@ def get_provenance_record( caption: str, ancestors: list, long_names: list, - plot_types: bool | list, - statistics: bool | list, + plot_types: list = None, + statistics: list = None, ) -> dict: """Get provenance record. @@ -232,9 +234,9 @@ def get_provenance_record( "long_names": long_names, "ancestors": ancestors, } - if plot_types is not False: + if plot_types: record["plot_types"] = plot_types - if statistics is not False: + if statistics: record["statistics"] = statistics return record @@ -317,11 +319,11 @@ def write_mapping_dict(work_dir: str, dataset: str, mapping_dict: dict): """ mapping_dict_reformat = convert_dict(mapping_dict) - with open( + with Path( f"{work_dir}/wt_mapping_dict_{dataset}.json", "w", encoding="utf-8", - ) as file: + ).open("w", encoding="utf-8") as file: json.dump(mapping_dict_reformat, file) @@ -536,6 +538,6 @@ def check_mapping_dict_format(mapping_dict: dict) -> dict: ------- dict: mapping dict in {lwt: slwt, ...} format """ - if isinstance(mapping_dict.get(list(mapping_dict.keys())[0]), list): + if isinstance(mapping_dict[next(iter(mapping_dict))], list): return reverse_convert_dict(mapping_dict) return mapping_dict From 3b71b3790966d17d96313bfb8889cfbd77af784b Mon Sep 17 00:00:00 2001 From: Thomas Kroissenbrunner Date: Mon, 1 Dec 2025 13:40:43 +0100 Subject: [PATCH 19/57] some medium issues --- esmvaltool/diag_scripts/weathertyping/calc_utils.py | 4 ++-- .../diag_scripts/weathertyping/weathertyping.py | 4 ++-- esmvaltool/diag_scripts/weathertyping/wt_utils.py | 13 +++++++------ 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/esmvaltool/diag_scripts/weathertyping/calc_utils.py b/esmvaltool/diag_scripts/weathertyping/calc_utils.py index 3221e8c12f..a4dd0293a2 100644 --- a/esmvaltool/diag_scripts/weathertyping/calc_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/calc_utils.py @@ -439,7 +439,7 @@ def calc_lwt_slwt_model( cfg: dict, cube: iris.cube.Cube, data_info: dict, - predefined_slwt: dict = None, + predefined_slwt: dict | None = None, ): """Calculate Lamb as well as simplified weathertypes for model and write them to file. @@ -458,7 +458,7 @@ def calc_lwt_slwt_model( f"{cfg.get('work_dir')}/{data_info.get('output_file_path')}", ).exists(): Path( - f"{cfg.get('work_dir')}/{data_info.get('output_file_path')}" + f"{cfg.get('work_dir')}/{data_info.get('output_file_path')}", ).mkdir( parents=True, exist_ok=True, diff --git a/esmvaltool/diag_scripts/weathertyping/weathertyping.py b/esmvaltool/diag_scripts/weathertyping/weathertyping.py index 2ef83b3137..f991cb04ff 100644 --- a/esmvaltool/diag_scripts/weathertyping/weathertyping.py +++ b/esmvaltool/diag_scripts/weathertyping/weathertyping.py @@ -175,8 +175,8 @@ def process_era5_automatic_slwt( def run_automatic_slwt(cfg: dict): - """Run the automated calculation for simplified weathertypes \ - and write to file, and plot the means and seasonal occurrence \ + """Run the automated calculation for simplified weathertypes + and write to file, and plot the means and seasonal occurrence of the weathertypes. Args: diff --git a/esmvaltool/diag_scripts/weathertyping/wt_utils.py b/esmvaltool/diag_scripts/weathertyping/wt_utils.py index eb4e982bbb..63939df256 100644 --- a/esmvaltool/diag_scripts/weathertyping/wt_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/wt_utils.py @@ -200,7 +200,8 @@ def load_wt_files(path: str, mode="simplified"): lwt_cube = iris.load_cube(path, "lwt") wt_cubes = [lwt_cube] else: - raise ValueError("Mode not recognized. Use 'simplified' or 'lamb'.") + e = "Mode not recognized. Use 'simplified' or 'lamb'." + raise ValueError(e) return wt_cubes @@ -209,8 +210,8 @@ def get_provenance_record( caption: str, ancestors: list, long_names: list, - plot_types: list = None, - statistics: list = None, + plot_types: list | None = None, + statistics: list | None = None, ) -> dict: """Get provenance record. @@ -218,8 +219,8 @@ def get_provenance_record( caption (str): Caption of plot ancestors (list): List of ancestor plots long_names (list): List of variable long names - plot_types (bool | list): Type of plot - statistics (bool | list): Types of statistics used + plot_types (list | None): Type of plot + statistics (list | None): Types of statistics used Returns ------- @@ -291,7 +292,7 @@ def get_mapping_dict(selected_pairs: list) -> dict: mapping_array = [] for elem in selected_pairs: - (mapping_array.append(elem[0]),) + mapping_array.append(elem[0]) s = [set(i) for i in mapping_array if i] From e8df48b08afbde0e8bf6af2558f337fcb001e137 Mon Sep 17 00:00:00 2001 From: Thomas Kroissenbrunner Date: Mon, 1 Dec 2025 14:01:50 +0100 Subject: [PATCH 20/57] refactor wt_algorithm --- .../diag_scripts/weathertyping/calc_utils.py | 163 +++++++++++------- .../diag_scripts/weathertyping/wt_utils.py | 2 - 2 files changed, 105 insertions(+), 60 deletions(-) diff --git a/esmvaltool/diag_scripts/weathertyping/calc_utils.py b/esmvaltool/diag_scripts/weathertyping/calc_utils.py index a4dd0293a2..2fe5dee458 100644 --- a/esmvaltool/diag_scripts/weathertyping/calc_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/calc_utils.py @@ -92,11 +92,9 @@ def calc_slwt_obs( timerange, ) - with open( + with Path( f"{cfg.get('work_dir')}/wt_selected_pairs_{dataset}.json", - "w", - encoding="utf-8", - ) as file: + ).open("w", encoding="utf-8") as file: json.dump(selected_pairs, file) mapping_dict = get_mapping_dict(selected_pairs) @@ -293,6 +291,99 @@ def calc_total_shear_velocity( return westerly_shear_velocity + southerly_shear_velocity +def lamp_pure_directional_type( + direction: float, weathertypes: np.array, i: int +) -> int: + """Calculate Lamp pure directional weathertype. + + Args: + ---- + direction (float): direction in degrees + + Returns + ------- + int: Lamb weathertype + """ + if direction >= 337.5 or direction < 22.5: + weathertypes[i] = 1 + if 22.5 <= direction < 67.5: + weathertypes[i] = 2 + if 67.5 <= direction < 112.5: + weathertypes[i] = 3 + if 112.5 <= direction < 157.5: + weathertypes[i] = 4 + if 157.5 <= direction < 202.5: + weathertypes[i] = 5 + if 202.5 <= direction < 247.5: + weathertypes[i] = 6 + if 247.5 <= direction < 292.5: + weathertypes[i] = 7 + if 292.5 <= direction < 337.5: + weathertypes[i] = 8 + + +def lamp_synoptic_directional_type( + direction: float, weathertypes: np.array, i: int +): + """Calculate Lamp synoptic/directional hybrid weathertype. + + Args: + ---- + direction (float): direction in degrees + + Returns + ------- + int: Lamb weathertype + """ + if direction >= 337.5 or direction < 22.5: + weathertypes[i] = 11 + if 22.5 <= direction < 67.5: + weathertypes[i] = 12 + if 67.5 <= direction < 112.5: + weathertypes[i] = 13 + if 112.5 <= direction < 157.5: + weathertypes[i] = 14 + if 157.5 <= direction < 202.5: + weathertypes[i] = 15 + if 202.5 <= direction < 247.5: + weathertypes[i] = 16 + if 247.5 <= direction < 292.5: + weathertypes[i] = 17 + if 292.5 <= direction < 337.5: + weathertypes[i] = 18 + + +def lamb_synoptic_directional_type_zlt0( + direction: float, weathertypes: np.array, i: int +): + """Calculate Lamb synoptic/directional hybrid weathertype with z <0. + + Args: + ---- + direction (float): direction in degrees + + Returns + ------- + int: Lamb weathertype + """ + if direction >= 337.5 or direction < 22.5: + weathertypes[i] = 19 + if 22.5 <= direction < 67.5: + weathertypes[i] = 20 + if 67.5 <= direction < 112.5: + weathertypes[i] = 21 + if 112.5 <= direction < 157.5: + weathertypes[i] = 22 + if 157.5 <= direction < 202.5: + weathertypes[i] = 23 + if 202.5 <= direction < 247.5: + weathertypes[i] = 24 + if 247.5 <= direction < 292.5: + weathertypes[i] = 25 + if 292.5 <= direction < 337.5: + weathertypes[i] = 26 + + def wt_algorithm(cube: iris.cube.Cube, dataset: str) -> np.array: """Algorithm to calculate Lamb weathertypes. @@ -368,22 +459,8 @@ def wt_algorithm(cube: iris.cube.Cube, dataset: str) -> np.array: # Lamb pure directional type if abs(z_i) < total_flow[i]: - if direction >= 337.5 or direction < 22.5: - weathertypes[i] = 1 - elif 22.5 <= direction < 67.5: - weathertypes[i] = 2 - elif 67.5 <= direction < 112.5: - weathertypes[i] = 3 - elif 112.5 <= direction < 157.5: - weathertypes[i] = 4 - elif 157.5 <= direction < 202.5: - weathertypes[i] = 5 - elif 202.5 <= direction < 247.5: - weathertypes[i] = 6 - elif 247.5 <= direction < 292.5: - weathertypes[i] = 7 - elif 292.5 <= direction < 337.5: - weathertypes[i] = 8 + # writes directly to weathertypes array + lamp_pure_directional_type(direction, weathertypes, i) # Lamb pure cyclonic and anticyclonic type elif (2 * total_flow[i]) < abs(z_i): if z_i > 0: @@ -394,40 +471,12 @@ def wt_algorithm(cube: iris.cube.Cube, dataset: str) -> np.array: # Lamb synoptic/direction hybrid types elif total_flow[i] < abs(z_i) < (2 * total_flow[i]): if z_i > 0: - if direction >= 337.5 or direction < 22.5: - weathertypes[i] = 11 - elif 22.5 <= direction < 67.5: - weathertypes[i] = 12 - elif 67.5 <= direction < 112.5: - weathertypes[i] = 13 - elif 112.5 <= direction < 157.5: - weathertypes[i] = 14 - elif 157.5 <= direction < 202.5: - weathertypes[i] = 15 - elif 202.5 <= direction < 247.5: - weathertypes[i] = 16 - elif 247.5 <= direction < 292.5: - weathertypes[i] = 17 - elif 292.5 <= direction < 337.5: - weathertypes[i] = 18 + # writes directly to weathertypes array + lamp_synoptic_directional_type(direction, weathertypes, i) elif z_i < 0: - if direction >= 337.5 or direction < 22.5: - weathertypes[i] = 19 - elif 22.5 <= direction < 67.5: - weathertypes[i] = 20 - elif 67.5 <= direction < 112.5: - weathertypes[i] = 21 - elif 112.5 <= direction < 157.5: - weathertypes[i] = 22 - elif 157.5 <= direction < 202.5: - weathertypes[i] = 23 - elif 202.5 <= direction < 247.5: - weathertypes[i] = 24 - elif 247.5 <= direction < 292.5: - weathertypes[i] = 25 - elif 292.5 <= direction < 337.5: - weathertypes[i] = 26 + # writes directly to weathertypes array + lamb_synoptic_directional_type_zlt0(direction, weathertypes, i) # light indeterminate flow, corresponding to Lamb unclassified type U elif abs(z_i) < 6 and total_flow[i] < 6: weathertypes[i] = 27 @@ -475,16 +524,14 @@ def calc_lwt_slwt_model( ) if not predefined_slwt: - with open( + with Path( f"{cfg.get('work_dir')}/wt_mapping_dict_ERA5.json", - encoding="utf-8", - ) as file: + ).open("r", encoding="utf-8") as file: mapping_dict_era5_f = json.load(file) - with open( + with Path( f"{cfg.get('work_dir')}/wt_mapping_dict_E-OBS.json", - encoding="utf-8", - ) as file: + ).open("r", encoding="utf-8") as file: mapping_dict_eobs_f = json.load(file) mapping_dict_era5 = reverse_convert_dict(mapping_dict_era5_f) mapping_dict_eobs = reverse_convert_dict(mapping_dict_eobs_f) diff --git a/esmvaltool/diag_scripts/weathertyping/wt_utils.py b/esmvaltool/diag_scripts/weathertyping/wt_utils.py index 63939df256..b43e448a08 100644 --- a/esmvaltool/diag_scripts/weathertyping/wt_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/wt_utils.py @@ -322,8 +322,6 @@ def write_mapping_dict(work_dir: str, dataset: str, mapping_dict: dict): with Path( f"{work_dir}/wt_mapping_dict_{dataset}.json", - "w", - encoding="utf-8", ).open("w", encoding="utf-8") as file: json.dump(mapping_dict_reformat, file) From e2b5013e9dc149a6f1c2ee3bcc2a36e6ad587e2d Mon Sep 17 00:00:00 2001 From: Thomas Kroissenbrunner Date: Mon, 1 Dec 2025 14:24:52 +0100 Subject: [PATCH 21/57] magic values --- .../diag_scripts/weathertyping/calc_utils.py | 74 +++++++++++-------- .../weathertyping/weathertyping.py | 8 +- .../diag_scripts/weathertyping/wt_utils.py | 10 +-- 3 files changed, 53 insertions(+), 39 deletions(-) diff --git a/esmvaltool/diag_scripts/weathertyping/calc_utils.py b/esmvaltool/diag_scripts/weathertyping/calc_utils.py index 2fe5dee458..c7181c047c 100644 --- a/esmvaltool/diag_scripts/weathertyping/calc_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/calc_utils.py @@ -5,6 +5,7 @@ import logging import warnings from pathlib import Path +from typing import Final # to manipulate iris cubes import iris @@ -36,6 +37,17 @@ warnings.filterwarnings("ignore", ".*Collapsing a non-contiguous coordinate*") +DIR_0: Final = 0.0 +DIR_22_5: Final = 22.5 +DIR_67_5: Final = 67.5 +DIR_112_5: Final = 112.5 +DIR_157_5: Final = 157.5 +DIR_202_5: Final = 202.5 +DIR_247_5: Final = 247.5 +DIR_292_5: Final = 292.5 +DIR_337_5: Final = 337.5 + + def calc_slwt_obs( cfg: dict, lwt: np.array, @@ -304,21 +316,21 @@ def lamp_pure_directional_type( ------- int: Lamb weathertype """ - if direction >= 337.5 or direction < 22.5: + if direction >= DIR_337_5 or direction < DIR_22_5: weathertypes[i] = 1 - if 22.5 <= direction < 67.5: + if DIR_22_5 <= direction < DIR_67_5: weathertypes[i] = 2 - if 67.5 <= direction < 112.5: + if DIR_67_5 <= direction < DIR_112_5: weathertypes[i] = 3 - if 112.5 <= direction < 157.5: + if DIR_112_5 <= direction < DIR_157_5: weathertypes[i] = 4 - if 157.5 <= direction < 202.5: + if DIR_157_5 <= direction < DIR_202_5: weathertypes[i] = 5 - if 202.5 <= direction < 247.5: + if DIR_202_5 <= direction < DIR_247_5: weathertypes[i] = 6 - if 247.5 <= direction < 292.5: + if DIR_247_5 <= direction < DIR_292_5: weathertypes[i] = 7 - if 292.5 <= direction < 337.5: + if DIR_292_5 <= direction < DIR_337_5: weathertypes[i] = 8 @@ -335,21 +347,21 @@ def lamp_synoptic_directional_type( ------- int: Lamb weathertype """ - if direction >= 337.5 or direction < 22.5: + if direction >= DIR_337_5 or direction < DIR_22_5: weathertypes[i] = 11 - if 22.5 <= direction < 67.5: + if DIR_22_5 <= direction < DIR_67_5: weathertypes[i] = 12 - if 67.5 <= direction < 112.5: + if DIR_67_5 <= direction < DIR_112_5: weathertypes[i] = 13 - if 112.5 <= direction < 157.5: + if DIR_112_5 <= direction < DIR_157_5: weathertypes[i] = 14 - if 157.5 <= direction < 202.5: + if DIR_157_5 <= direction < DIR_202_5: weathertypes[i] = 15 - if 202.5 <= direction < 247.5: + if DIR_202_5 <= direction < DIR_247_5: weathertypes[i] = 16 - if 247.5 <= direction < 292.5: + if DIR_247_5 <= direction < DIR_292_5: weathertypes[i] = 17 - if 292.5 <= direction < 337.5: + if DIR_292_5 <= direction < DIR_337_5: weathertypes[i] = 18 @@ -366,21 +378,21 @@ def lamb_synoptic_directional_type_zlt0( ------- int: Lamb weathertype """ - if direction >= 337.5 or direction < 22.5: + if direction >= DIR_337_5 or direction < DIR_22_5: weathertypes[i] = 19 - if 22.5 <= direction < 67.5: + if DIR_22_5 <= direction < DIR_67_5: weathertypes[i] = 20 - if 67.5 <= direction < 112.5: + if DIR_67_5 <= direction < DIR_112_5: weathertypes[i] = 21 - if 112.5 <= direction < 157.5: + if DIR_112_5 <= direction < DIR_157_5: weathertypes[i] = 22 - if 157.5 <= direction < 202.5: + if DIR_157_5 <= direction < DIR_202_5: weathertypes[i] = 23 - if 202.5 <= direction < 247.5: + if DIR_202_5 <= direction < DIR_247_5: weathertypes[i] = 24 - if 247.5 <= direction < 292.5: + if DIR_247_5 <= direction < DIR_292_5: weathertypes[i] = 25 - if 292.5 <= direction < 337.5: + if DIR_292_5 <= direction < DIR_337_5: weathertypes[i] = 26 @@ -454,7 +466,7 @@ def wt_algorithm(cube: iris.cube.Cube, dataset: str) -> np.array: if southerly_flow[i] >= 0: direction += 180 # deg - if direction < 0: + if direction < DIR_0: direction += 360 # deg # Lamb pure directional type @@ -980,7 +992,7 @@ def plot_means( preproc_var: np.array, wt_cubes: iris.cube.Cube, data_info: dict, - only_lwt=False, + mode: str = "slwt", ): """Plot means, anomalies and standard deviations. @@ -990,16 +1002,18 @@ def plot_means( preproc_var (np.array): Preprocessed variable cube wt_cubes (iris.cube.Cube): List of cubes of lwt, slwt_ERA5 and slwt_EOBS data_info (dict): Dictionary with info to dataset - only_lwt (bool, optional): If True, only lwt means are calculated. - If False, lwt, slwt_ERA5 and slwt_EOBS means are calculated. Defaults to False. + mode (str, optional): Mode of weathertype calculation, either "slwt" or "lwt". Defaults to "slwt". """ - if not only_lwt: + if mode == "slwt": data_info["wt_string"] = "lwt" calc_wt_means(cfg, preproc_var, wt_cubes, data_info) data_info["wt_string"] = "slwt_ERA5" calc_wt_means(cfg, preproc_var, wt_cubes, data_info) data_info["wt_string"] = "slwt_EOBS" calc_wt_means(cfg, preproc_var, wt_cubes, data_info) - else: + elif mode == "lwt": data_info["wt_string"] = "lwt" calc_wt_means(cfg, preproc_var, wt_cubes, data_info) + else: + e = "mode must be either 'slwt' or 'lwt'" + raise ValueError(e) diff --git a/esmvaltool/diag_scripts/weathertyping/weathertyping.py b/esmvaltool/diag_scripts/weathertyping/weathertyping.py index f991cb04ff..a6a420b893 100644 --- a/esmvaltool/diag_scripts/weathertyping/weathertyping.py +++ b/esmvaltool/diag_scripts/weathertyping/weathertyping.py @@ -244,7 +244,7 @@ def process_era5_lwt(preproc_variables_dict, cfg, dataset_vars, data_info): # load wt files wt_cubes = load_wt_files( f"{cfg.get('work_dir')}/{data_info['dataset']}.nc", - mode="lamb", + mode="lwt", ) if cfg.get("plotting", False): @@ -256,7 +256,7 @@ def process_era5_lwt(preproc_variables_dict, cfg, dataset_vars, data_info): data_info["var"] = var_name data_info["preproc_path"] = var_data[1] - plot_means(cfg, var_data[0], wt_cubes, data_info, only_lwt=True) + plot_means(cfg, var_data[0], wt_cubes, data_info, "lwt") plot_seasonal_occurrence(cfg, wt_cubes, data_info) @@ -297,7 +297,7 @@ def process_models_lwt(cfg: dict, dataset_vars: list, data_info: dict): f"{data_info['driver']}" f"_{data_info['ensemble']}_" f"{data_info['timerange']}.nc", - mode="lamb", + mode="lwt", ) var_dict = get_looping_dict( @@ -315,7 +315,7 @@ def process_models_lwt(cfg: dict, dataset_vars: list, data_info: dict): var_data[0], wt_cubes, data_info, - only_lwt=True, + "lwt", ) plot_seasonal_occurrence(cfg, wt_cubes, data_info) diff --git a/esmvaltool/diag_scripts/weathertyping/wt_utils.py b/esmvaltool/diag_scripts/weathertyping/wt_utils.py index b43e448a08..dc736c0e46 100644 --- a/esmvaltool/diag_scripts/weathertyping/wt_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/wt_utils.py @@ -180,27 +180,27 @@ def get_looping_dict(preproc_vars: list): } -def load_wt_files(path: str, mode="simplified"): +def load_wt_files(path: str, mode="slwt"): """Load wt files. Args: path (str): Path of wt file - mode (str, optional): Type of weathertype to load. Defaults to "simplified". + mode (str, optional): Type of weathertype to load. Defaults to "slwt". Returns ------- list: List of weathertype cubes. """ - if mode == "simplified": + if mode == "slwt": lwt_cube = iris.load_cube(path, "lwt") slwt_era5_cube = iris.load_cube(path, "slwt_era5") slwt_eobs_cube = iris.load_cube(path, "slwt_eobs") wt_cubes = [lwt_cube, slwt_era5_cube, slwt_eobs_cube] - elif mode == "lamb": + elif mode == "lwt": lwt_cube = iris.load_cube(path, "lwt") wt_cubes = [lwt_cube] else: - e = "Mode not recognized. Use 'simplified' or 'lamb'." + e = "Mode not recognized. Use 'slwt' or 'lwt'." raise ValueError(e) return wt_cubes From 899aca19717ad11032148894a7fd26632c8e9921 Mon Sep 17 00:00:00 2001 From: Thomas Kroissenbrunner Date: Mon, 1 Dec 2025 14:30:14 +0100 Subject: [PATCH 22/57] minor --- .../diag_scripts/weathertyping/calc_utils.py | 18 ++++++++++++++---- .../diag_scripts/weathertyping/wt_utils.py | 5 +---- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/esmvaltool/diag_scripts/weathertyping/calc_utils.py b/esmvaltool/diag_scripts/weathertyping/calc_utils.py index c7181c047c..b0ccb36fdc 100644 --- a/esmvaltool/diag_scripts/weathertyping/calc_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/calc_utils.py @@ -37,6 +37,7 @@ warnings.filterwarnings("ignore", ".*Collapsing a non-contiguous coordinate*") +# defining constants for direction calculations DIR_0: Final = 0.0 DIR_22_5: Final = 22.5 DIR_67_5: Final = 67.5 @@ -47,6 +48,9 @@ DIR_292_5: Final = 292.5 DIR_337_5: Final = 337.5 +# defining constant for flow limit +FLOW_LIMIT: Final = 6 + def calc_slwt_obs( cfg: dict, @@ -304,7 +308,9 @@ def calc_total_shear_velocity( def lamp_pure_directional_type( - direction: float, weathertypes: np.array, i: int + direction: float, + weathertypes: np.array, + i: int, ) -> int: """Calculate Lamp pure directional weathertype. @@ -335,7 +341,9 @@ def lamp_pure_directional_type( def lamp_synoptic_directional_type( - direction: float, weathertypes: np.array, i: int + direction: float, + weathertypes: np.array, + i: int, ): """Calculate Lamp synoptic/directional hybrid weathertype. @@ -366,7 +374,9 @@ def lamp_synoptic_directional_type( def lamb_synoptic_directional_type_zlt0( - direction: float, weathertypes: np.array, i: int + direction: float, + weathertypes: np.array, + i: int, ): """Calculate Lamb synoptic/directional hybrid weathertype with z <0. @@ -490,7 +500,7 @@ def wt_algorithm(cube: iris.cube.Cube, dataset: str) -> np.array: # writes directly to weathertypes array lamb_synoptic_directional_type_zlt0(direction, weathertypes, i) # light indeterminate flow, corresponding to Lamb unclassified type U - elif abs(z_i) < 6 and total_flow[i] < 6: + elif abs(z_i) < FLOW_LIMIT and total_flow[i] < FLOW_LIMIT: weathertypes[i] = 27 return weathertypes diff --git a/esmvaltool/diag_scripts/weathertyping/wt_utils.py b/esmvaltool/diag_scripts/weathertyping/wt_utils.py index dc736c0e46..629a0f6e49 100644 --- a/esmvaltool/diag_scripts/weathertyping/wt_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/wt_utils.py @@ -289,10 +289,7 @@ def get_mapping_dict(selected_pairs: list) -> dict: ------- dict: Mapping dictionary """ - mapping_array = [] - - for elem in selected_pairs: - mapping_array.append(elem[0]) + mapping_array = [elem[0] for elem in selected_pairs] s = [set(i) for i in mapping_array if i] From b18c427156c69646b53e72ad63aa3a8f7f8780aa Mon Sep 17 00:00:00 2001 From: Thomas Kroissenbrunner Date: Mon, 1 Dec 2025 15:54:05 +0100 Subject: [PATCH 23/57] refactor plot_maps --- .../diag_scripts/weathertyping/plot_utils.py | 54 ++++++++++++------- .../weathertyping/weathertyping.py | 4 +- 2 files changed, 36 insertions(+), 22 deletions(-) diff --git a/esmvaltool/diag_scripts/weathertyping/plot_utils.py b/esmvaltool/diag_scripts/weathertyping/plot_utils.py index c2fadd2a61..4ea72c1cb6 100644 --- a/esmvaltool/diag_scripts/weathertyping/plot_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/plot_utils.py @@ -157,6 +157,40 @@ def plot_seasonal_occurrence( plt.close() +def set_gridlines(ax: plt.Axes): + """Set gridlines for plotting maps. + + Args: + ---- + ax (plt.Axes): Axes object to draw gridlines on. + + Returns + ------- + gl (ax.gridlines): Gridlines object + """ + gl = ax.gridlines( + crs=ccrs.PlateCarree(), + draw_labels=True, + linewidth=0.5, + color="gray", + alpha=0.5, + linestyle="--", + ) + gl.left_labels = True + gl.bottom_labels = True + gl.top_labels = False + gl.right_labels = False + gl.xlines = True + gl.ylocator = mticker.FixedLocator(np.arange(20, 70, 5)) + gl.xlocator = mticker.FixedLocator([-10, -5, 0, 5, 10, 15]) + gl.xformatter = LONGITUDE_FORMATTER + gl.yformatter = LATITUDE_FORMATTER + gl.xlabel_style = {"size": 8, "color": "black"} + gl.ylabel_style = {"color": "black", "size": 8} + + return gl + + def plot_maps( wt: np.array, cfg: dict, @@ -228,25 +262,7 @@ def plot_maps( cb.ax.tick_params(labelsize=8) cb.set_label(label=f"{var_name} {mode} {unit}") - gl = ax.gridlines( - crs=ccrs.PlateCarree(), - draw_labels=True, - linewidth=0.5, - color="gray", - alpha=0.5, - linestyle="--", - ) - gl.left_labels = True - gl.bottom_labels = True - gl.top_labels = False - gl.right_labels = False - gl.xlines = True - gl.ylocator = mticker.FixedLocator(np.arange(20, 70, 5)) - gl.xlocator = mticker.FixedLocator([-10, -5, 0, 5, 10, 15]) - gl.xformatter = LONGITUDE_FORMATTER - gl.yformatter = LATITUDE_FORMATTER - gl.xlabel_style = {"size": 8, "color": "black"} - gl.ylabel_style = {"color": "black", "size": 8} + set_gridlines(ax) ax.set_extent([-15, 20, 27.5, 62.5]) diff --git a/esmvaltool/diag_scripts/weathertyping/weathertyping.py b/esmvaltool/diag_scripts/weathertyping/weathertyping.py index a6a420b893..63496d6a6e 100644 --- a/esmvaltool/diag_scripts/weathertyping/weathertyping.py +++ b/esmvaltool/diag_scripts/weathertyping/weathertyping.py @@ -175,9 +175,7 @@ def process_era5_automatic_slwt( def run_automatic_slwt(cfg: dict): - """Run the automated calculation for simplified weathertypes - and write to file, and plot the means and seasonal occurrence - of the weathertypes. + """Run the automated calculation for simplified weathertypes and write to file, and plot the means and seasonal occurrence of the weathertypes. Args: ---- From 3273db43a3bb01cc8861adb97d3ac75305f658b6 Mon Sep 17 00:00:00 2001 From: Thomas Kroissenbrunner Date: Mon, 1 Dec 2025 16:03:20 +0100 Subject: [PATCH 24/57] refactor plot_maps --- .../diag_scripts/weathertyping/plot_utils.py | 105 +++++++++++------- 1 file changed, 65 insertions(+), 40 deletions(-) diff --git a/esmvaltool/diag_scripts/weathertyping/plot_utils.py b/esmvaltool/diag_scripts/weathertyping/plot_utils.py index 4ea72c1cb6..b5ca7fa21a 100644 --- a/esmvaltool/diag_scripts/weathertyping/plot_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/plot_utils.py @@ -191,6 +191,61 @@ def set_gridlines(ax: plt.Axes): return gl +def prepare_plot_title( + data_info: dict, wt: int, var_name: str, mode: str +) -> str: + """Return formatted plot title. + + Args: + ---- + data_info (dict): dict containing info on dataset + wt (int): weathertype + var_name (str): name of variable + mode (str): statistic used + + Returns + ------- + (str): title of plot + """ + ensemble = data_info.get("ensemble", "") + timerange = data_info.get("timerange") + dataset = data_info.get("dataset") + if var_name == "pr" and dataset == "ERA5": + title = f"{dataset} {ensemble}, total {var_name} {mode}\n{timerange}, wt: {wt}" + elif var_name == "pr": + title = f"{dataset} {ensemble}, {var_name} flux {mode}\n{timerange}, wt: {wt}" + elif var_name == "tas": + title = f"{dataset} {ensemble}, 1000 hPa {var_name} {mode}\n{timerange}, wt: {wt}" + else: # psl or others + title = ( + f"{dataset} {ensemble}, {var_name} {mode}\n{timerange}, wt: {wt}" + ) + return title + + +def get_unit(var_name: str, dataset: str) -> str: + """Get unit of variables. + + Args: + ---- + var_name (str): name of variable + dataset (str): name of dataset + + Returns + ------- + (str): unit of variable + """ + if var_name == "psl": + return "[hPa]" + if var_name == "pr" and dataset == "ERA5": + return "[m]" + if var_name == "pr": + return "[kg m-2 s-1]" + if var_name == "tas": + return "[K]" + return "" + + def plot_maps( wt: np.array, cfg: dict, @@ -221,46 +276,16 @@ def plot_maps( ax = plt.axes(projection=ccrs.PlateCarree()) - if data_info.get("var") == "psl": - psl_cmap = get_colormap("psl") - plt.title( - f"{data_info.get('dataset')} {data_info.get('ensemble', '')}, {data_info.get('var')} {mode}\n" - f"{data_info.get('timerange')}, wt: {wt}", - ) - unit = "[hPa]" - im = iplt.contourf(cube / 100, cmap=psl_cmap) - cb = plt.colorbar(im) - cb.ax.tick_params(labelsize=8) - cb.set_label(label=f"{var_name} {mode} {unit}") - elif var_name == "pr": - prcp_cmap = get_colormap("prcp") - if data_info.get("dataset") == "ERA5": - unit = "[m]" - plt.title( - f"{data_info.get('dataset')} {data_info.get('ensemble', '')}, total {var_name} {mode}\n" - f"{data_info.get('timerange')}, wt: {wt}", - ) - else: - unit = "[kg m-2 s-1]" - plt.title( - f"{data_info.get('dataset')} {data_info.get('ensemble', '')}, {var_name} flux {mode}\n" - f"{data_info.get('timerange')}, wt: {wt}", - ) - im = iplt.contourf(cube, cmap=prcp_cmap) - cb = plt.colorbar(im) - cb.ax.tick_params(labelsize=8) - cb.set_label(label=f"{var_name} {mode} {unit}") - elif var_name == "tas": - temp_cmap = get_colormap("temp") - unit = "[K]" - plt.title( - f"{data_info.get('dataset')} {data_info.get('ensemble', '')}, 1000 hPa {var_name} {mode}\n" - f"{data_info.get('timerange')}, wt: {wt}", - ) - im = iplt.contourf(cube, cmap=temp_cmap) - cb = plt.colorbar(im) - cb.ax.tick_params(labelsize=8) - cb.set_label(label=f"{var_name} {mode} {unit}") + cmap = get_colormap(var_name if var_name != "pr" else "prcp") + + title = prepare_plot_title(data_info, wt, var_name, mode) + plt.title(title) + unit = get_unit(var_name, data_info.get("dataset")) + + im = iplt.contourf(cube if var_name != "psl" else cube / 100, cmap=cmap) + cb = plt.colorbar(im) + cb.ax.tick_params(labelsize=8) + cb.set_label(label=f"{var_name} {mode} {unit}") set_gridlines(ax) From ed31fdb21a5b5a61ccd6c6037acff5f163b841b6 Mon Sep 17 00:00:00 2001 From: Thomas Kroissenbrunner Date: Mon, 1 Dec 2025 16:10:24 +0100 Subject: [PATCH 25/57] minor --- esmvaltool/diag_scripts/weathertyping/plot_utils.py | 5 ++++- esmvaltool/diag_scripts/weathertyping/weathertyping.py | 5 +---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/esmvaltool/diag_scripts/weathertyping/plot_utils.py b/esmvaltool/diag_scripts/weathertyping/plot_utils.py index b5ca7fa21a..bb9b574e03 100644 --- a/esmvaltool/diag_scripts/weathertyping/plot_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/plot_utils.py @@ -192,7 +192,10 @@ def set_gridlines(ax: plt.Axes): def prepare_plot_title( - data_info: dict, wt: int, var_name: str, mode: str + data_info: dict, + wt: int, + var_name: str, + mode: str, ) -> str: """Return formatted plot title. diff --git a/esmvaltool/diag_scripts/weathertyping/weathertyping.py b/esmvaltool/diag_scripts/weathertyping/weathertyping.py index 63496d6a6e..c0ca748f01 100644 --- a/esmvaltool/diag_scripts/weathertyping/weathertyping.py +++ b/esmvaltool/diag_scripts/weathertyping/weathertyping.py @@ -319,10 +319,7 @@ def process_models_lwt(cfg: dict, dataset_vars: list, data_info: dict): def run_lwt(cfg: dict): - """Run calculation of weathertypes. - Write to file, and plot the means of psl, tas, and pr \ - for each weathertype. \ - Plot seasonal occurrence of the weathertypes. + """Run calculation of weathertypes. Write to file, and plot the means of psl, tas, and pr for each weathertype. Plot seasonal occurrence of the weathertypes. Args: ------- From 3d551dabc619f7ff9923fc521fdbac436b69fe9d Mon Sep 17 00:00:00 2001 From: Thomas Kroissenbrunner Date: Thu, 4 Dec 2025 09:51:22 +0100 Subject: [PATCH 26/57] minor changes for provenance handling --- .../diag_scripts/weathertyping/calc_utils.py | 3 +- .../diag_scripts/weathertyping/plot_utils.py | 31 ++++++++++++++++--- .../diag_scripts/weathertyping/wt_utils.py | 4 ++- 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/esmvaltool/diag_scripts/weathertyping/calc_utils.py b/esmvaltool/diag_scripts/weathertyping/calc_utils.py index b0ccb36fdc..5dc1aea5de 100644 --- a/esmvaltool/diag_scripts/weathertyping/calc_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/calc_utils.py @@ -732,8 +732,7 @@ def calc_wt_means( f"{cfg.get('work_dir')}/ERA5.nc", ] provenance_record = get_provenance_record( - f"{var_name} means for \ - {wt_string}", + f"{var_name} means for {wt_string}", ancestors, [var_name], ["map"], diff --git a/esmvaltool/diag_scripts/weathertyping/plot_utils.py b/esmvaltool/diag_scripts/weathertyping/plot_utils.py index bb9b574e03..60537981c3 100644 --- a/esmvaltool/diag_scripts/weathertyping/plot_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/plot_utils.py @@ -23,7 +23,7 @@ # local imports from cartopy.mpl.gridliner import LATITUDE_FORMATTER, LONGITUDE_FORMATTER from matplotlib.colors import ListedColormap -from wt_utils import get_driver +from wt_utils import get_driver, get_provenance_record, log_provenance iris.FUTURE.datum_support = True @@ -69,8 +69,8 @@ def plot_seasonal_occurrence( output_path = f"{cfg['plot_dir']}/seasonal_occurrence" - if not Path.exists(f"{output_path}"): - Path(f"{output_path}").mkdir(parents=True, exist_ok=True) + if not Path(output_path).exists(): + Path(output_path).mkdir(parents=True, exist_ok=True) month_list = [ "Jan", @@ -271,7 +271,7 @@ def plot_maps( logger.info( "Plotting %s %s %s for %s %s", data_info.get("dataset"), - data_info.get("var"), + var_name, mode, data_info.get("wt_string"), wt, @@ -313,6 +313,29 @@ def plot_maps( ) plt.close() + # log provenance + ancestors = [ + f"{data_info.get('preproc_path')}", + f"{cfg.get('work_dir')}/ERA5.nc", + ] + provenance_record = get_provenance_record( + f"{var_name} {mode} for {data_info.get('wt_string')}", + ancestors, + [var_name], + ["map"], + [mode], + ) + + local_path = f"{cfg.get('plot_dir')}/{mode}" + + log_provenance( + f"{local_path}/{data_info.get('wt_string')}_{wt}{get_driver(data_info)}_" + f"{data_info.get('dataset')}_{data_info.get('ensemble', '')}" + f"_{var_name}_{mode}_{data_info.get('timerange')}.png", + cfg, + provenance_record, + ) + def plot_corr_rmse_heatmaps( cfg: dict, diff --git a/esmvaltool/diag_scripts/weathertyping/wt_utils.py b/esmvaltool/diag_scripts/weathertyping/wt_utils.py index 629a0f6e49..43911a1e6b 100644 --- a/esmvaltool/diag_scripts/weathertyping/wt_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/wt_utils.py @@ -254,7 +254,9 @@ def log_provenance(filename: str, cfg: dict, provenance_record: dict): with ProvenanceLogger(cfg) as provenance_logger: provenance_logger.log(filename, provenance_record) - logger.info("Output stored as %s", filename) + logger.info( + "Provenance added to %s", f"{cfg['run_dir']}/diagnostic_provenance.yml" + ) def turn_list_to_mapping_dict(list_: list) -> dict: From d8128c695d44ec42af2761fbb58485d85e396644 Mon Sep 17 00:00:00 2001 From: Thomas Kroissenbrunner Date: Thu, 4 Dec 2025 09:53:47 +0100 Subject: [PATCH 27/57] trailing comma --- esmvaltool/diag_scripts/weathertyping/wt_utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/esmvaltool/diag_scripts/weathertyping/wt_utils.py b/esmvaltool/diag_scripts/weathertyping/wt_utils.py index 43911a1e6b..e5a4600855 100644 --- a/esmvaltool/diag_scripts/weathertyping/wt_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/wt_utils.py @@ -255,7 +255,8 @@ def log_provenance(filename: str, cfg: dict, provenance_record: dict): provenance_logger.log(filename, provenance_record) logger.info( - "Provenance added to %s", f"{cfg['run_dir']}/diagnostic_provenance.yml" + "Provenance added to %s", + f"{cfg['run_dir']}/diagnostic_provenance.yml", ) From 52e7e59683fc446bb9581a6fd6952a9f7955adb3 Mon Sep 17 00:00:00 2001 From: Thomas Kroissenbrunner Date: Thu, 4 Dec 2025 10:33:34 +0100 Subject: [PATCH 28/57] . --- esmvaltool/diag_scripts/weathertyping/wt_utils.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/esmvaltool/diag_scripts/weathertyping/wt_utils.py b/esmvaltool/diag_scripts/weathertyping/wt_utils.py index e5a4600855..5ac2eb7896 100644 --- a/esmvaltool/diag_scripts/weathertyping/wt_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/wt_utils.py @@ -29,6 +29,7 @@ def get_driver(data_info: dict) -> str: """Get driving model name and string for further use. Args: + ---- data_info (dict): Data information dictionary. Returns @@ -42,6 +43,7 @@ def load_wt_preprocessors(dataset: str, preproc_variables_dict: dict): """Load preprocessor cubes for calculating Lamb weathertypes. Args: + ---- dataset (str): Name of dataset preproc_variables_dict (dict): Dictionary with info on preprocessor variables @@ -73,6 +75,7 @@ def get_ancestors_era5_eobs(dataset: str, preproc_variables_dict: dict): """Get ancestors for observational data. Args: + ---- dataset (str): Name of dataset preproc_variables_dict (dict): Dictionary with info on preprocessor variables @@ -102,6 +105,7 @@ def get_model_output_filepath(dataset: str, data_info: list): """Get output filepaths for models. Args: + ---- dataset (str): Name of dataset data_info (list): Model variables @@ -124,6 +128,7 @@ def get_preproc_lists(preproc_vars: list): """Put preprocessors and paths into lists for further use. Args: + ---- preproc_vars (list): List of preprocessor variables. Returns @@ -148,6 +153,7 @@ def get_preproc_lists_ensemble(preproc_vars: list): """Put preprocessors and paths into lists for further use. Args: + ---- preproc_vars (list): List of preprocessor variables. Returns @@ -165,6 +171,7 @@ def get_looping_dict(preproc_vars: list): """Put variable preprocessors into dict for looping. Args: + ---- preproc_vars (list): List of preprocessor variables. Returns @@ -184,6 +191,7 @@ def load_wt_files(path: str, mode="slwt"): """Load wt files. Args: + ---- path (str): Path of wt file mode (str, optional): Type of weathertype to load. Defaults to "slwt". @@ -216,6 +224,7 @@ def get_provenance_record( """Get provenance record. Args: + ---- caption (str): Caption of plot ancestors (list): List of ancestor plots long_names (list): List of variable long names @@ -264,6 +273,7 @@ def turn_list_to_mapping_dict(list_: list) -> dict: """Turn list of combined WT to a dictionary for further use. Args: + ---- list_ (list): List of combined WTs Returns @@ -286,6 +296,7 @@ def get_mapping_dict(selected_pairs: list) -> dict: """Get mapping dictionary from list of selected pairs. Args: + ---- selected_pairs (list): List of selected weathertype pairs Returns From 4405f42217c2265a7bf4ba26aaf73335953eabed Mon Sep 17 00:00:00 2001 From: Thomas Kroissenbrunner Date: Tue, 9 Dec 2025 08:14:56 +0100 Subject: [PATCH 29/57] rm copilot and codacy-cli files, rm comments for imports --- .codacy/cli.sh | 149 ---------------- .codacy/codacy.yaml | 4 - .github/copilot-instructions.md | 165 ------------------ .../diag_scripts/weathertyping/calc_utils.py | 6 - .../diag_scripts/weathertyping/plot_utils.py | 8 - .../weathertyping/weathertyping.py | 1 - .../diag_scripts/weathertyping/wt_utils.py | 5 - 7 files changed, 338 deletions(-) delete mode 100755 .codacy/cli.sh delete mode 100644 .codacy/codacy.yaml delete mode 100644 .github/copilot-instructions.md diff --git a/.codacy/cli.sh b/.codacy/cli.sh deleted file mode 100755 index d5438cdd31..0000000000 --- a/.codacy/cli.sh +++ /dev/null @@ -1,149 +0,0 @@ -#!/usr/bin/env bash - - -set -e +o pipefail - -# Set up paths first -bin_name="codacy-cli-v2" - -# Determine OS-specific paths -os_name=$(uname) -arch=$(uname -m) - -case "$arch" in -"x86_64") - arch="amd64" - ;; -"x86") - arch="386" - ;; -"aarch64"|"arm64") - arch="arm64" - ;; -esac - -if [ -z "$CODACY_CLI_V2_TMP_FOLDER" ]; then - if [ "$(uname)" = "Linux" ]; then - CODACY_CLI_V2_TMP_FOLDER="$HOME/.cache/codacy/codacy-cli-v2" - elif [ "$(uname)" = "Darwin" ]; then - CODACY_CLI_V2_TMP_FOLDER="$HOME/Library/Caches/Codacy/codacy-cli-v2" - else - CODACY_CLI_V2_TMP_FOLDER=".codacy-cli-v2" - fi -fi - -version_file="$CODACY_CLI_V2_TMP_FOLDER/version.yaml" - - -get_version_from_yaml() { - if [ -f "$version_file" ]; then - local version=$(grep -o 'version: *"[^"]*"' "$version_file" | cut -d'"' -f2) - if [ -n "$version" ]; then - echo "$version" - return 0 - fi - fi - return 1 -} - -get_latest_version() { - local response - if [ -n "$GH_TOKEN" ]; then - response=$(curl -Lq --header "Authorization: Bearer $GH_TOKEN" "https://api.github.com/repos/codacy/codacy-cli-v2/releases/latest" 2>/dev/null) - else - response=$(curl -Lq "https://api.github.com/repos/codacy/codacy-cli-v2/releases/latest" 2>/dev/null) - fi - - handle_rate_limit "$response" - local version=$(echo "$response" | grep -m 1 tag_name | cut -d'"' -f4) - echo "$version" -} - -handle_rate_limit() { - local response="$1" - if echo "$response" | grep -q "API rate limit exceeded"; then - fatal "Error: GitHub API rate limit exceeded. Please try again later" - fi -} - -download_file() { - local url="$1" - - echo "Downloading from URL: ${url}" - if command -v curl > /dev/null 2>&1; then - curl -# -LS "$url" -O - elif command -v wget > /dev/null 2>&1; then - wget "$url" - else - fatal "Error: Could not find curl or wget, please install one." - fi -} - -download() { - local url="$1" - local output_folder="$2" - - ( cd "$output_folder" && download_file "$url" ) -} - -download_cli() { - # OS name lower case - suffix=$(echo "$os_name" | tr '[:upper:]' '[:lower:]') - - local bin_folder="$1" - local bin_path="$2" - local version="$3" - - if [ ! -f "$bin_path" ]; then - echo "📥 Downloading CLI version $version..." - - remote_file="codacy-cli-v2_${version}_${suffix}_${arch}.tar.gz" - url="https://github.com/codacy/codacy-cli-v2/releases/download/${version}/${remote_file}" - - download "$url" "$bin_folder" - tar xzfv "${bin_folder}/${remote_file}" -C "${bin_folder}" - fi -} - -# Warn if CODACY_CLI_V2_VERSION is set and update is requested -if [ -n "$CODACY_CLI_V2_VERSION" ] && [ "$1" = "update" ]; then - echo "⚠️ Warning: Performing update with forced version $CODACY_CLI_V2_VERSION" - echo " Unset CODACY_CLI_V2_VERSION to use the latest version" -fi - -# Ensure version.yaml exists and is up to date -if [ ! -f "$version_file" ] || [ "$1" = "update" ]; then - echo "ℹ️ Fetching latest version..." - version=$(get_latest_version) - mkdir -p "$CODACY_CLI_V2_TMP_FOLDER" - echo "version: \"$version\"" > "$version_file" -fi - -# Set the version to use -if [ -n "$CODACY_CLI_V2_VERSION" ]; then - version="$CODACY_CLI_V2_VERSION" -else - version=$(get_version_from_yaml) -fi - - -# Set up version-specific paths -bin_folder="${CODACY_CLI_V2_TMP_FOLDER}/${version}" - -mkdir -p "$bin_folder" -bin_path="$bin_folder"/"$bin_name" - -# Download the tool if not already installed -download_cli "$bin_folder" "$bin_path" "$version" -chmod +x "$bin_path" - -run_command="$bin_path" -if [ -z "$run_command" ]; then - fatal "Codacy cli v2 binary could not be found." -fi - -if [ "$#" -eq 1 ] && [ "$1" = "download" ]; then - echo "Codacy cli v2 download succeeded" -else - eval "$run_command $*" -fi diff --git a/.codacy/codacy.yaml b/.codacy/codacy.yaml deleted file mode 100644 index c2cccd0da6..0000000000 --- a/.codacy/codacy.yaml +++ /dev/null @@ -1,4 +0,0 @@ -runtimes: - - node@22.2.0 -tools: - - eslint@8.57.0 diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md deleted file mode 100644 index 80925ee934..0000000000 --- a/.github/copilot-instructions.md +++ /dev/null @@ -1,165 +0,0 @@ -## Quick context for AI code contributors - -This repository contains ESMValTool — a community climate-model diagnostics and -preprocessing framework. The goal of these notes is to give an AI agent the -minimum, high-value knowledge to be productive immediately: how the project is -structured, common developer workflows, and concrete examples to follow. - -### Quick setup (developer environment) - -- Prefer Conda/Mamba using the provided environment files at the repository root: - -```bash -# create the dev environment (mamba recommended) -mamba env create --name esmvaltool --file environment.yml -conda activate esmvaltool -# or use environment_python.yml for a lighter python-only env -``` - -- Docker: the repository includes `docker/Dockerfile`. Build from the repo root: - -```bash -docker build -t esmvaltool:latest . -f docker/Dockerfile -``` - -- Installation for local development: pip install editable mode and tests extras - -```bash -pip install -e .[test] -pytest # run tests (pytest options are configured in pyproject.toml) -``` - -### Where to run diagnostics / recipes - -- Recipes and diagnostics are described in the README and docs. Diagnostics - live under `esmvaltool/diag_scripts/`. Example diagnostic: - `esmvaltool/diag_scripts/weathertyping/weathertyping.py`. - -### Architecture & major components (short) - -- `esmvaltool/` — main package. Diagnostics and helper code live here. - - `diag_scripts/` — each diagnostic in its own sub-folder. Diagnostics are - Python scripts but may call NCL/R tools. - - `cmorizers/` — scripts to convert observational datasets to CMOR format. -- `ESMValTool_stuff/`, `preval/` — user recipes, examples, and auxiliary - analysis code. Keep changes to these under separate branches unless they are - core fixes. -- External integration: heavy reliance on `ESMValCore` (preprocessing), Iris - (`scitools-iris`) for cube handling, and many scientific packages (see - `pyproject.toml` and `environment.yml`). Changes to dataflow must respect - ESMValCore's preprocessing contracts. - -### Diagnostic implementation conventions (concrete patterns) - -- Entry pattern: diagnostics obtain config via the shared context manager: - -```python -from esmvaltool.diag_scripts.shared import run_diagnostic - -if __name__ == '__main__': - with run_diagnostic() as config: - run_my_diagnostic(config) -``` - -Follow the `weathertyping` example: functions take the `cfg` dict returned by -`run_diagnostic()` and extract preprocessor variables through helper functions -in the same diagnostic package. - -- File I/O: diagnostics generally read/write netCDF files (Iris cubes). Use the - existing helper utilities in `diag_scripts//` (e.g., `wt_utils`, - `calc_utils`) instead of re-implementing low-level file-handling. - -### Testing, linting and formatting specifics - -- Tests: configured through `pyproject.toml`. After `pip install -e .[test]` run - `pytest`. The project uses 79-character line length. -- Linting: `ruff` + `prospector` (see `pyproject.toml`). Prefer automatic fixes - where safe (the repo config sets `fix = true` for ruff). - -### Packaging and runtime notes to keep in mind - -- The package expects Python >= 3.11 (see `pyproject.toml`). -- `esmvaltool/__init__.py` raises a helpful error if the package is not installed - editable — prefer `pip install -e .` for local imports and tests. -- Many dependencies come from conda-forge and some (e.g., ESMPy) are not on - PyPI. For CI-like runs prefer the provided `environment.yml` or the Docker - image. - -### Helpful repository-specific search patterns for agents - -- Diagnostics: `esmvaltool/diag_scripts/**/` — look for `run_diagnostic` use. -- Recipes/user examples: `ESMValTool_stuff/` and `ESMValTool_output/`. -- Environment and build: `environment.yml`, `environment_python.yml`, - `docker/Dockerfile`, `pyproject.toml`. - -### What to change and what to avoid - -- Safe: small diagnostic improvements, refactors within a diagnostic folder, - tests for new behavior, documentation updates to README/docs snippets. -- Avoid: large changes to preprocessing contracts, switching major dependency - versions, or edits to recipes in `ESMValTool_stuff/` that alter user data - provenance without discussing with maintainers. - -### Quick examples to cite in PRs - -- Point reviewers to `esmvaltool/diag_scripts/weathertyping/weathertyping.py` - as a model for diagnostic layout and `docker/Dockerfile` and - `environment.yml` for environment reproducibility. - -### When to ask a human - -- If a change affects: the public API of preprocessing (ESMValCore contracts), - Docker/CI pipelines, or requires new conda-build recipes. Also ask if you - need access to large datasets (CMIP/obs) that are not in the repo. - -If anything here is unclear or you'd like a shorter/longer variant, tell me -which parts to expand and I'll iterate. - - -# Copilot Agents for VSCode - -## Auto-Fix Codacy Minor Issues Agent - -This agent is designed to assist developers by automatically fixing **Codacy minor issues** in code, using GitHub Copilot suggestions within VSCode. - -### Overview - -- **Purpose:** Automatically detect and fix minor code quality issues flagged by Codacy. -- **Scope:** Minor issues only (e.g., formatting, unused imports, variable naming, unnecessary semicolons). -- **Toolchain:** VSCode + GitHub Copilot + Codacy CLI (optional for local linting). -- **Trigger:** On file save or on-demand. - -### Setup - -1. **Install VSCode Extensions** - - [GitHub Copilot](https://marketplace.visualstudio.com/items?itemName=GitHub.copilot) - - [Codacy VSCode Extension](https://marketplace.visualstudio.com/items?itemName=Codacy.codacy) - - Optional: Linter/formatter for your language (e.g., ESLint, Prettier, Black). - -2. **Enable Copilot Suggestions** - - Go to VSCode `Settings` → `Copilot` → Enable inline suggestions. - - Enable `Accept Copilot suggestions on save` for automated workflow (optional, may require keybinding). - -3. **Codacy CLI (optional)** - - Install Codacy CLI: - ```bash - npm install -g @codacy/cli - ``` - - Run to check issues locally: - ```bash - codacy analyze - ``` - -### Workflow - -1. Open your project in VSCode. -2. Open the file flagged with minor Codacy issues. -3. Accept Copilot inline suggestions to automatically fix formatting, variable names, or other minor issues. -4. Save the file to trigger any auto-formatters configured. -5. Run Codacy analysis to ensure issues are resolved. - -### Example Use Cases - -- **Unused import:** - ```python - import os # Minor issue: unused import diff --git a/esmvaltool/diag_scripts/weathertyping/calc_utils.py b/esmvaltool/diag_scripts/weathertyping/calc_utils.py index 5dc1aea5de..43d65b0a80 100644 --- a/esmvaltool/diag_scripts/weathertyping/calc_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/calc_utils.py @@ -1,21 +1,15 @@ """Utility functions for calculations.""" -# operating system manipulations (e.g. path constructions) import json import logging import warnings from pathlib import Path from typing import Final -# to manipulate iris cubes import iris import iris.analysis.cartography - -# general imports import numpy as np import pandas as pd - -# local imports from plot_utils import plot_corr_rmse_heatmaps, plot_maps from wt_utils import ( check_mapping_dict_format, diff --git a/esmvaltool/diag_scripts/weathertyping/plot_utils.py b/esmvaltool/diag_scripts/weathertyping/plot_utils.py index 60537981c3..86f1dd0830 100644 --- a/esmvaltool/diag_scripts/weathertyping/plot_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/plot_utils.py @@ -1,26 +1,18 @@ """Utility functions for plotting.""" -# operating system manipulations (e.g. path constructions) import logging import warnings from pathlib import Path -# plotting imports import cartopy.crs as ccrs import cartopy.feature as cfeature - -# to manipulate iris cubes import iris import iris.analysis.cartography import iris.plot as iplt import matplotlib.pyplot as plt import matplotlib.ticker as mticker - -# general imports import numpy as np import seaborn as sns - -# local imports from cartopy.mpl.gridliner import LATITUDE_FORMATTER, LONGITUDE_FORMATTER from matplotlib.colors import ListedColormap from wt_utils import get_driver, get_provenance_record, log_provenance diff --git a/esmvaltool/diag_scripts/weathertyping/weathertyping.py b/esmvaltool/diag_scripts/weathertyping/weathertyping.py index c0ca748f01..18f9bd298c 100644 --- a/esmvaltool/diag_scripts/weathertyping/weathertyping.py +++ b/esmvaltool/diag_scripts/weathertyping/weathertyping.py @@ -7,7 +7,6 @@ import iris -# import internal esmvaltool modules here from esmvaltool.diag_scripts.shared import group_metadata, run_diagnostic from esmvaltool.diag_scripts.weathertyping.calc_utils import ( calc_lwt_model, diff --git a/esmvaltool/diag_scripts/weathertyping/wt_utils.py b/esmvaltool/diag_scripts/weathertyping/wt_utils.py index 5ac2eb7896..3eee1eec94 100644 --- a/esmvaltool/diag_scripts/weathertyping/wt_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/wt_utils.py @@ -1,20 +1,15 @@ """Utility functions for weathertyping script.""" -# operating system manipulations (e.g. path constructions) import json import logging import warnings from pathlib import Path -# to manipulate iris cubes import iris import iris.analysis.cartography - -# general imports import numpy as np import pandas as pd -# import internal esmvaltool modules here from esmvaltool.diag_scripts.shared import ProvenanceLogger iris.FUTURE.datum_support = True From 1ae08c68e9374083d9a2c8898569d29c48eb08f2 Mon Sep 17 00:00:00 2001 From: Thomas Kroissenbrunner Date: Tue, 9 Dec 2025 08:34:16 +0100 Subject: [PATCH 30/57] change docstrings to numpy style --- .../weathertyping/weathertyping.py | 86 ++++++++++++------- 1 file changed, 54 insertions(+), 32 deletions(-) diff --git a/esmvaltool/diag_scripts/weathertyping/weathertyping.py b/esmvaltool/diag_scripts/weathertyping/weathertyping.py index 18f9bd298c..b652aae780 100644 --- a/esmvaltool/diag_scripts/weathertyping/weathertyping.py +++ b/esmvaltool/diag_scripts/weathertyping/weathertyping.py @@ -40,11 +40,14 @@ def process_models_automatic_slwt( ): """Process model data for calculating Lamb and simplified weathertypes. - Args: - ---- - cfg (dict): Nested dictionary of metadata - dataset_vars (list): List of variable dictionaries for a specific dataset - data_info (dict): Dictionary holding dataset information. + Parameters + ---------- + cfg: + Nested dictionary of metadata + dataset_vars: + List of variable dictionaries for a specific dataset + data_info: + Dictionary holding dataset information. """ for ensemble_var in dataset_vars: if ensemble_var.get("preprocessor") == "weathertype_preproc": @@ -104,12 +107,16 @@ def process_era5_automatic_slwt( ): """Process ERA5 data for calculating Lamb and simplified weathertypes. - Args: - ---- - data_info (dict): Dictionary holding dataset information. - preproc_variables_dict (dict): Dictionary holding preprocessed variables for all datasets. - cfg (dict): Nested dictionary of metadata - dataset_vars (list): List of variable dictionaries for a specific dataset + Parameters + ---------- + data_info: + Dictionary holding dataset information. + preproc_variables_dict: + Dictionary holding preprocessed variables for all datasets. + cfg: + Nested dictionary of metadata + dataset_vars: + List of variable dictionaries for a specific dataset """ wt_preproc, wt_preproc_prcp, wt_preproc_prcp_eobs = load_wt_preprocessors( data_info["dataset"], @@ -176,9 +183,10 @@ def process_era5_automatic_slwt( def run_automatic_slwt(cfg: dict): """Run the automated calculation for simplified weathertypes and write to file, and plot the means and seasonal occurrence of the weathertypes. - Args: - ---- - cfg (dict): Nested dictionary of metadata + Parameters + ---------- + cfg: + Nested dictionary of metadata """ preproc_variables_dict = group_metadata( cfg.get("input_data").values(), @@ -202,15 +210,24 @@ def run_automatic_slwt(cfg: dict): process_models_automatic_slwt(cfg, dataset_vars, data_info) -def process_era5_lwt(preproc_variables_dict, cfg, dataset_vars, data_info): +def process_era5_lwt( + preproc_variables_dict: dict, + cfg: dict, + dataset_vars: list, + data_info: dict, +): """Process ERA5 data for calculating Lamb weathertypes. - Args: - ------- - preproc_variables_dict (dict): Dictionary holding preprocessed variables for all datasets. - cfg (dict): Nested dictionary of metadata - dataset_vars (list): List of variable dictionaries for a specific dataset - data_info (dict): Dictionary holding dataset information. + Parameters + ---------- + preproc_variables_dict: + Dictionary holding preprocessed variables for all datasets. + cfg: + Nested dictionary of metadata + dataset_vars: + List of variable dictionaries for a specific dataset + data_info: + Dictionary holding dataset information. """ wt_preproc, _, _ = load_wt_preprocessors( data_info["dataset"], @@ -260,11 +277,14 @@ def process_era5_lwt(preproc_variables_dict, cfg, dataset_vars, data_info): def process_models_lwt(cfg: dict, dataset_vars: list, data_info: dict): """Process model data for calculating Lamb weathertypes. - Args: - ------- - cfg (dict): Nested dictionary of metadata - dataset_vars (list): List of variable dictionaries for a specific dataset - data_info (dict): Dictionary holding dataset information. + Parameters + ---------- + cfg: + Nested dictionary of metadata + dataset_vars: + List of variable dictionaries for a specific dataset + data_info: + Dictionary holding dataset information. """ for ensemble_var in dataset_vars: if ensemble_var.get("preprocessor") == "weathertype_preproc": @@ -320,9 +340,10 @@ def process_models_lwt(cfg: dict, dataset_vars: list, data_info: dict): def run_lwt(cfg: dict): """Run calculation of weathertypes. Write to file, and plot the means of psl, tas, and pr for each weathertype. Plot seasonal occurrence of the weathertypes. - Args: - ------- - cfg (dict): Nested dictionary of metadata + Parameters + ---------- + cfg: + Nested dictionary of metadata """ preproc_variables_dict = group_metadata( cfg.get("input_data").values(), @@ -350,9 +371,10 @@ def run_lwt(cfg: dict): def run_my_diagnostic(cfg: dict): """Run the weathertyping diagnostic. - Args: - ------- - cfg (dict): Nested dictionary of metadata + Parameters + ---------- + cfg: + Nested dictionary of metadata """ automatic_slwt = cfg.get("automatic_slwt") From a4c476d01dcda5b5cf464562b351bea2eac88bff Mon Sep 17 00:00:00 2001 From: Thomas Kroissenbrunner Date: Tue, 9 Dec 2025 09:01:41 +0100 Subject: [PATCH 31/57] change docstrings to numpy style --- .../diag_scripts/weathertyping/calc_utils.py | 280 ++++++++++------- .../diag_scripts/weathertyping/plot_utils.py | 113 ++++--- .../weathertyping/weathertyping.py | 52 ++-- .../diag_scripts/weathertyping/wt_utils.py | 285 +++++++++++------- 4 files changed, 447 insertions(+), 283 deletions(-) diff --git a/esmvaltool/diag_scripts/weathertyping/calc_utils.py b/esmvaltool/diag_scripts/weathertyping/calc_utils.py index 43d65b0a80..21a645f2d9 100644 --- a/esmvaltool/diag_scripts/weathertyping/calc_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/calc_utils.py @@ -56,14 +56,20 @@ def calc_slwt_obs( ) -> np.array: """Calculate simplified weathertypes for observational data. - Args: - ---- - cfg (dict): Configuration dictionary from recipe - lwt (np.array): Array of Lamb WT - cube (iris.cube.Cube): Cube of psl data - dataset (str): Name of dataset - ancestors (list): List of ancestors - timerange (str): Time range for the calculation + Parameters + ---------- + cfg + Configuration dictionary from recipe + lwt + Array of Lamb WT + cube + Cube of psl data + dataset + Name of dataset + ancestors + List of ancestors + timerange + Time range for the calculation Returns ------- @@ -136,7 +142,8 @@ def calc_const(): Returns ------- - tuple: The four constants needed for WT calculation. + tuple + The four constants needed for WT calculation. """ const1 = 1 / np.cos(45 * np.pi / 180) const2 = np.sin(45 * np.pi / 180) / np.sin(40 * np.pi / 180) @@ -154,12 +161,15 @@ def calc_westerly_flow(cube: iris.cube.Cube) -> np.array: scheme. Int. J. Climatol., 13: 655-663. https://doi.org/10.1002/joc.3370130606 - Args: - cube (iris.cube.Cube): Cube of psl data. + Parameters + ---------- + cube + Cube of psl data. Returns ------- - np.array: westerly flow + np.array + westerly flow """ return 1 / 2 * (cube.data[:, 1, 2] + cube.data[:, 1, 4]) - 1 / 2 * ( cube.data[:, 3, 2] + cube.data[:, 3, 4] @@ -174,13 +184,17 @@ def calc_southerly_flow(cube: iris.cube.Cube, const1: float) -> np.array: scheme. Int. J. Climatol., 13: 655-663. https://doi.org/10.1002/joc.3370130606 - Args: - cube (iris.cube.Cube): cube of psl data - const1 (float): const1 + Parameters + ---------- + cube + cube of psl data + const1 + const1 Returns ------- - np.array: southerly flow + np.array + southerly flow """ return const1 * ( 1 @@ -203,13 +217,17 @@ def calc_resultant_flow( scheme. Int. J. Climatol., 13: 655-663. https://doi.org/10.1002/joc.3370130606 - Args: - westerly_flow (np.array): westerly flow - southerly_flow (np.array): southerly flow + Parameters + ---------- + westerly_flow + westerly flow + southerly_flow + southerly flow Returns ------- - np.array: resultant flow + np.array + resultant flow """ return (southerly_flow**2 + westerly_flow**2) ** (1 / 2) @@ -226,14 +244,19 @@ def calc_westerly_shear_velocity( scheme. Int. J. Climatol., 13: 655-663. https://doi.org/10.1002/joc.3370130606 - Args: - cube (iris.cube.Cube): cube of psl data - const2 (float): const2 - const3 (float): const3 + Parameters + ---------- + cube + cube of psl data + const2 + const2 + const3 + const3 Returns ------- - np.array: westerly shear velocity + np.array + westerly shear velocity """ return const2 * ( 1 / 2 * (cube.data[:, 0, 2] + cube.data[:, 0, 4]) @@ -255,13 +278,17 @@ def calc_southerly_shear_velocity( scheme. Int. J. Climatol., 13: 655-663. https://doi.org/10.1002/joc.3370130606 - Args: - cube (iris.cube.Cube): cube of psl data - const4 (float): const + Parameters + ---------- + cube + cube of psl data + const4 + const Returns ------- - np.array: southerly shear velocity + np.array + southerly shear velocity """ return const4 * ( 1 @@ -290,13 +317,17 @@ def calc_total_shear_velocity( scheme. Int. J. Climatol., 13: 655-663. https://doi.org/10.1002/joc.3370130606 - Args: - westerly_shear_velocity (np.array): westerly shear velocity - southerly_shear_velocity (np.array): southerly shear velocity + Parameters + ---------- + westerly_shear_velocity + westerly shear velocity + southerly_shear_velocity + southerly shear velocity Returns ------- - np.array:total shear velocity + np.array + total shear velocity """ return westerly_shear_velocity + southerly_shear_velocity @@ -308,13 +339,15 @@ def lamp_pure_directional_type( ) -> int: """Calculate Lamp pure directional weathertype. - Args: - ---- - direction (float): direction in degrees + Parameters + ---------- + direction + direction in degrees Returns ------- - int: Lamb weathertype + int + Lamb weathertype """ if direction >= DIR_337_5 or direction < DIR_22_5: weathertypes[i] = 1 @@ -338,16 +371,18 @@ def lamp_synoptic_directional_type( direction: float, weathertypes: np.array, i: int, -): +) -> int: """Calculate Lamp synoptic/directional hybrid weathertype. - Args: - ---- - direction (float): direction in degrees + Parameters + ---------- + direction + direction in degrees Returns ------- - int: Lamb weathertype + int + Lamb weathertype """ if direction >= DIR_337_5 or direction < DIR_22_5: weathertypes[i] = 11 @@ -371,16 +406,18 @@ def lamb_synoptic_directional_type_zlt0( direction: float, weathertypes: np.array, i: int, -): +) -> int: """Calculate Lamb synoptic/directional hybrid weathertype with z <0. - Args: - ---- - direction (float): direction in degrees + Parameters + ---------- + direction + direction in degrees Returns ------- - int: Lamb weathertype + int + Lamb weathertype """ if direction >= DIR_337_5 or direction < DIR_22_5: weathertypes[i] = 19 @@ -408,14 +445,17 @@ def wt_algorithm(cube: iris.cube.Cube, dataset: str) -> np.array: scheme. Int. J. Climatol., 13: 655-663. https://doi.org/10.1002/joc.3370130606 - Args: - ---- - cube (iris.cube.Cube): Cube of psl data - dataset (str): Name of dataset + Parameters + ---------- + cube + Cube of psl data + dataset + Name of dataset Returns ------- - np.array: Lamb weathertypes + np.array + Lamb weathertypes """ # lats and lons corresponding to datapoints # 55, 5 -> 1 @@ -508,13 +548,17 @@ def calc_lwt_slwt_model( ): """Calculate Lamb as well as simplified weathertypes for model and write them to file. - Args: - ------- - cfg (dict): Configuration dictionary from recipe - cube (iris.cube.Cube): PSL field of dataset - data_info (dict): Dictionary with info to dataset - predefined_slwt (dict | None): If None, automatic_slwt will be used. - If dict, this mapping dict will be used. + Parameters + ---------- + cfg + Configuration dictionary from recipe + cube + PSL field of dataset + data_info + Dictionary with info to dataset + predefined_slwt + If None, automatic_slwt will be used. + If dict, this mapping dict will be used. (see recipe option predefined_slwt) """ driver = get_driver(data_info) @@ -621,16 +665,20 @@ def process_prcp_mean( ) -> list: """Return which weathertypes can be grouped together for a certain precipitation pattern. - Args: - ---- - cfg (dict): Configuration dictionary from recipe - data (np.array): Array of precipitation means for each WT - dataset (str): Name of dataset - timerange (str): Time range of dataset - + Parameters + ---------- + cfg + Configuration dictionary from recipe + data + Array of precipitation means for each WT + dataset + Name of dataset + timerange + Time range of dataset Returns ------- - list: Selected pairs of WT. This is passed to get_mapping_dict + list + Selected pairs of WT. This is passed to get_mapping_dict """ logger.info("Calculating corr and rsme matrices for %s", dataset) @@ -681,12 +729,16 @@ def calc_wt_means( ): """Calculate means for psl, tas or pr for weathertypes. - Args: - ---- - cfg (dict): Configuration dictionary from recipe - cube (iris.cube.Cube): Cube with variable data - wt_cubes (iris.cube.CubeList): List of cubes of lwt, slwt_ERA5 and slwt_EOBS - data_info (dict): Dictionary with info to dataset + Parameters + ---------- + cfg + Configuration dictionary from recipe + cube + Cube with variable data + wt_cubes + List of cubes of lwt, slwt_ERA5 and slwt_EOBS + data_info + Dictionary with info to dataset """ var_name = data_info.get("var") wt_string = data_info.get("wt_string") @@ -744,21 +796,25 @@ def calc_wt_means( ) -def get_wt_array(wt_string: str, wt_cubes: iris.cube.CubeList) -> np.array: +def get_wt_array(wt_string: str, wt_cubes: iris.cube.CubeList) -> tuple: """Get weathertype array and time coordinate based on wt_string. - Args: - ---- - wt_string(str): string for weathertype selection - wt_cubes(iris.cube.CubeList): list of weathertype cubes + Parameters + ---------- + wt_string + string for weathertype selection + wt_cubes + list of weathertype cubes Raises ------ - NameError: if wt_array does not exist for the given wt_string + NameError + if wt_array does not exist for the given wt_string Returns ------- - (np.array, np.array): weathertype array and time coordinate + tuple + weathertype array and time coordinate """ if wt_string == "slwt_ERA5": slwt_era5_cube = wt_cubes[1] @@ -789,12 +845,16 @@ def calc_wt_anomalies( ): """Calculate anomalies for psl, tas and pr for weathertypes. - Args: - ---- - cfg (dict): Configuration dictionary from recipe - cube (iris.cube.Cube): Cube with variable data - wt_cubes (iris.cube.CubeList): List of cubes of lwt, slwt_ERA5 and slwt_EOBS - data_info (dict): Dictionary with info to dataset + Parameters + ---------- + cfg + Configuration dictionary from recipe + cube + Cube with variable data + wt_cubes + List of cubes of lwt, slwt_ERA5 and slwt_EOBS + data_info + Dictionary with info to dataset """ var_name = data_info.get("var_name") wt_string = data_info.get("wt_string") @@ -862,12 +922,16 @@ def calc_wt_std( ): """Calculate standard deviation for psl, tas and pr for weathertypes. - Args: - ---- - cfg (dict): Configuration dictionary from recipe - cube (iris.cube.Cube): Cube with variable data - wt_cubes (iris.cube.CubeList): List of cubes of lwt, slwt_ERA5 and slwt_EOBS - data_info (dict): Dictionary with info to dataset + Parameters + ---------- + cfg + Configuration dictionary from recipe + cube + Cube with variable data + wt_cubes + List of cubes of lwt, slwt_ERA5 and slwt_EOBS + data_info + Dictionary with info to dataset """ var_name = data_info.get("var_name") wt_string = data_info.get("wt_string") @@ -927,11 +991,14 @@ def calc_wt_std( def calc_lwt_model(cfg: dict, cube: iris.cube.Cube, data_info: dict): """Calculate Lamb weathertypes for models. - Args: - ---- - cfg (dict): Configuration dictionary from recipe - cube (iris.cube.Cube): Cube with variable data - data_info (dict): Dictionary with info to dataset + Parameters + ---------- + cfg + Configuration dictionary from recipe + cube + Cube with variable data + data_info + Dictionary with info to dataset """ driver = get_driver(data_info) @@ -999,13 +1066,18 @@ def plot_means( ): """Plot means, anomalies and standard deviations. - Args: - ---- - cfg (dict): Configuration dictionary from recipe - preproc_var (np.array): Preprocessed variable cube - wt_cubes (iris.cube.Cube): List of cubes of lwt, slwt_ERA5 and slwt_EOBS - data_info (dict): Dictionary with info to dataset - mode (str, optional): Mode of weathertype calculation, either "slwt" or "lwt". Defaults to "slwt". + Parameters + ---------- + cfg + Configuration dictionary from recipe + preproc_var + Preprocessed variable cube + wt_cubes + List of cubes of lwt, slwt_ERA5 and slwt_EOBS + data_info + Dictionary with info to dataset + mode + Mode of weathertype calculation, either "slwt" or "lwt". Defaults to "slwt". """ if mode == "slwt": data_info["wt_string"] = "lwt" diff --git a/esmvaltool/diag_scripts/weathertyping/plot_utils.py b/esmvaltool/diag_scripts/weathertyping/plot_utils.py index 86f1dd0830..bb629ab9e1 100644 --- a/esmvaltool/diag_scripts/weathertyping/plot_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/plot_utils.py @@ -28,13 +28,15 @@ def generate_grayscale_hex_values(x): """Generate grayscale values for plotting seasonal occurences. - Args: - ---- - x (int): Amount of weathertypes. + Parameters + ---------- + x + Amount of weathertypes. Returns ------- - list: List of grayscale hex values + list + List of grayscale hex values """ grayscale_values = np.linspace(0, 1, x) @@ -51,11 +53,14 @@ def plot_seasonal_occurrence( ): """Plot seasonal occurences of weathertypes. - Args: - ---- - cfg (dict): Configuration dictionary from recipe - wt_cubes (iris.cube.Cube): List of cubes of lwt, slwt_ERA5 and slwt_EOBS - data_info (dict): Dictionary with info to dataset + Parameters + ---------- + cfg + Configuration dictionary from recipe + wt_cubes + List of cubes of lwt, slwt_ERA5 and slwt_EOBS + data_info + Dictionary with info to dataset """ driver = get_driver(data_info) @@ -152,13 +157,15 @@ def plot_seasonal_occurrence( def set_gridlines(ax: plt.Axes): """Set gridlines for plotting maps. - Args: - ---- - ax (plt.Axes): Axes object to draw gridlines on. + Parameters + ---------- + ax + Axes object to draw gridlines on. Returns ------- - gl (ax.gridlines): Gridlines object + gl : (ax.gridlines) + Gridlines object """ gl = ax.gridlines( crs=ccrs.PlateCarree(), @@ -191,16 +198,20 @@ def prepare_plot_title( ) -> str: """Return formatted plot title. - Args: - ---- - data_info (dict): dict containing info on dataset - wt (int): weathertype - var_name (str): name of variable - mode (str): statistic used - + Parameters + ---------- + data_info + dict containing info on dataset + wt + weathertype + var_name + name of variable + mode + statistic used Returns ------- - (str): title of plot + str + title of plot """ ensemble = data_info.get("ensemble", "") timerange = data_info.get("timerange") @@ -221,14 +232,17 @@ def prepare_plot_title( def get_unit(var_name: str, dataset: str) -> str: """Get unit of variables. - Args: - ---- - var_name (str): name of variable - dataset (str): name of dataset + Parameters + ---------- + var_name + name of variable + dataset + name of dataset Returns ------- - (str): unit of variable + str + unit of variable """ if var_name == "psl": return "[hPa]" @@ -250,13 +264,18 @@ def plot_maps( ): """Plot maps of means, std and anomalies. - Args: - ---- - wt (np.array): WT array - cfg (dict): Configuration dictionary from recipe - cube (iris.cube.Cube): Data to be plotted - data_info (dict): Dictionary with info to dataset - mode (str): Statistics that is used + Parameters + ---------- + wt + WT array + cfg + Configuration dictionary from recipe + cube + Data to be plotted + data_info + Dictionary with info to dataset + mode + Statistics that is used """ var_name = data_info.get("var") @@ -338,13 +357,18 @@ def plot_corr_rmse_heatmaps( ): """Plot correlation and rmse heatmaps. - Args: - ---- - cfg (dict): Configuration dictionary from recipe - pattern_correlation_matrix (np.array): Pattern correlation matrix - rmse_matrix (np.array): RMSE matrix - dataset (str): Name of dataset - timerange (str): Time range for the calculation + Parameters + ---------- + cf + gConfiguration dictionary from recipe + pattern_correlation_matrix + Pattern correlation matrix + rmse_matrix + RMSE matrix + dataset + Name of dataset + timerange + Time range for the calculation """ output_path = f"{cfg.get('plot_dir')}/heatmaps" @@ -417,12 +441,15 @@ def plot_corr_rmse_heatmaps( def get_colormap(colormap_string: str) -> ListedColormap: """Get colormaps for plottings. - Args: - colormap_string (str): string to identify colormap + Parameters + ---------- + colormap_string + string to identify colormap Returns ------- - ListedColormap: Colormap for the specified variable + ListedColormap + Colormap for the specified variable """ misc_seq_2_disc = [ (230 / 255, 240 / 255, 240 / 255), diff --git a/esmvaltool/diag_scripts/weathertyping/weathertyping.py b/esmvaltool/diag_scripts/weathertyping/weathertyping.py index b652aae780..c535a21849 100644 --- a/esmvaltool/diag_scripts/weathertyping/weathertyping.py +++ b/esmvaltool/diag_scripts/weathertyping/weathertyping.py @@ -42,11 +42,11 @@ def process_models_automatic_slwt( Parameters ---------- - cfg: - Nested dictionary of metadata - dataset_vars: - List of variable dictionaries for a specific dataset - data_info: + cfg + Nested dictionary of metadata. + dataset_vars + List of variable dictionaries for a specific dataset. + data_info Dictionary holding dataset information. """ for ensemble_var in dataset_vars: @@ -109,14 +109,14 @@ def process_era5_automatic_slwt( Parameters ---------- - data_info: + data_info Dictionary holding dataset information. - preproc_variables_dict: + preproc_variables_dict Dictionary holding preprocessed variables for all datasets. - cfg: - Nested dictionary of metadata - dataset_vars: - List of variable dictionaries for a specific dataset + cfg + Nested dictionary of metadata. + dataset_vars + List of variable dictionaries for a specific dataset. """ wt_preproc, wt_preproc_prcp, wt_preproc_prcp_eobs = load_wt_preprocessors( data_info["dataset"], @@ -185,7 +185,7 @@ def run_automatic_slwt(cfg: dict): Parameters ---------- - cfg: + cfg Nested dictionary of metadata """ preproc_variables_dict = group_metadata( @@ -220,13 +220,13 @@ def process_era5_lwt( Parameters ---------- - preproc_variables_dict: + preproc_variables_dict Dictionary holding preprocessed variables for all datasets. - cfg: - Nested dictionary of metadata - dataset_vars: - List of variable dictionaries for a specific dataset - data_info: + cfg + Nested dictionary of metadata. + dataset_vars + List of variable dictionaries for a specific dataset. + data_info Dictionary holding dataset information. """ wt_preproc, _, _ = load_wt_preprocessors( @@ -279,11 +279,11 @@ def process_models_lwt(cfg: dict, dataset_vars: list, data_info: dict): Parameters ---------- - cfg: - Nested dictionary of metadata - dataset_vars: - List of variable dictionaries for a specific dataset - data_info: + cfg + Nested dictionary of metadata. + dataset_vars + List of variable dictionaries for a specific dataset. + data_info Dictionary holding dataset information. """ for ensemble_var in dataset_vars: @@ -342,8 +342,8 @@ def run_lwt(cfg: dict): Parameters ---------- - cfg: - Nested dictionary of metadata + cfg + Nested dictionary of metadata. """ preproc_variables_dict = group_metadata( cfg.get("input_data").values(), @@ -373,7 +373,7 @@ def run_my_diagnostic(cfg: dict): Parameters ---------- - cfg: + cfg Nested dictionary of metadata """ automatic_slwt = cfg.get("automatic_slwt") diff --git a/esmvaltool/diag_scripts/weathertyping/wt_utils.py b/esmvaltool/diag_scripts/weathertyping/wt_utils.py index 3eee1eec94..cb74c64cbc 100644 --- a/esmvaltool/diag_scripts/weathertyping/wt_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/wt_utils.py @@ -23,28 +23,33 @@ def get_driver(data_info: dict) -> str: """Get driving model name and string for further use. - Args: - ---- - data_info (dict): Data information dictionary. + Parameters + ---------- + data_info + Data information dictionary. Returns ------- - str: Driver string with leading underscore or empty string. + str: + Driver string with leading underscore or empty string. """ return data_info.get("driver", "") -def load_wt_preprocessors(dataset: str, preproc_variables_dict: dict): +def load_wt_preprocessors(dataset: str, preproc_variables_dict: dict) -> tuple: """Load preprocessor cubes for calculating Lamb weathertypes. - Args: - ---- - dataset (str): Name of dataset - preproc_variables_dict (dict): Dictionary with info on preprocessor variables + Parameters + ---------- + dataset + Name of dataset + preproc_variables_dict + Dictionary with info on preprocessor variables Returns ------- - tuple: Preprocessor cubes for weathertyping + tuple + Preprocessor cubes for weathertyping """ wt_preproc = iris.load_cube( preproc_variables_dict.get(dataset)[0].get("filename"), @@ -66,17 +71,22 @@ def load_wt_preprocessors(dataset: str, preproc_variables_dict: dict): return wt_preproc, wt_preproc_prcp, wt_preproc_prcp_eobs -def get_ancestors_era5_eobs(dataset: str, preproc_variables_dict: dict): +def get_ancestors_era5_eobs( + dataset: str, preproc_variables_dict: dict +) -> tuple: """Get ancestors for observational data. - Args: - ---- - dataset (str): Name of dataset - preproc_variables_dict (dict): Dictionary with info on preprocessor variables + Parameters + ---------- + dataset + Name of dataset + preproc_variables_dict + Dictionary with info on preprocessor variables Returns ------- - tuple: Lists of ERA5 and E-OBS ancestors + tuple + Lists of ERA5 and E-OBS ancestors """ era5_ancestors = [ preproc_variables_dict.get(dataset)[0].get("filename"), @@ -96,17 +106,20 @@ def get_ancestors_era5_eobs(dataset: str, preproc_variables_dict: dict): return era5_ancestors, eobs_ancestors -def get_model_output_filepath(dataset: str, data_info: list): +def get_model_output_filepath(dataset: str, data_info: list) -> tuple: """Get output filepaths for models. - Args: - ---- - dataset (str): Name of dataset - data_info (list): Model variables + Parameters + ---------- + dataset + Name of dataset + data_info + Model variables Returns ------- - tuple: Output filepath and preprocessor path for future referencing. + tuple + Output filepath and preprocessor path for future referencing. """ timerange = data_info.get("timerange").replace("/", "-") experiment = data_info.get("exp") @@ -122,13 +135,15 @@ def get_model_output_filepath(dataset: str, data_info: list): def get_preproc_lists(preproc_vars: list): """Put preprocessors and paths into lists for further use. - Args: - ---- - preproc_vars (list): List of preprocessor variables. + Parameters + ---------- + preproc_vars + List of preprocessor variables. Returns ------- - tuple: Preprocessor cubes and paths. + tuple + Preprocessor cubes and paths. """ preproc_path_psl = preproc_vars[-3].get("filename") preproc_path_prcp = preproc_vars[-2].get("filename") @@ -147,13 +162,15 @@ def get_preproc_lists(preproc_vars: list): def get_preproc_lists_ensemble(preproc_vars: list): """Put preprocessors and paths into lists for further use. - Args: - ---- - preproc_vars (list): List of preprocessor variables. + Parameters + ---------- + preproc_vars + List of preprocessor variables. Returns ------- - tuple: Preprocessor cubes and paths. + tuple + Preprocessor cubes and paths. """ preproc_path = preproc_vars.get("filename") @@ -165,13 +182,15 @@ def get_preproc_lists_ensemble(preproc_vars: list): def get_looping_dict(preproc_vars: list): """Put variable preprocessors into dict for looping. - Args: - ---- - preproc_vars (list): List of preprocessor variables. + Parameters + ---------- + preproc_vars + List of preprocessor variables. Returns ------- - dict: Dictionary of preprocessor cubes and paths. + dict + Dictionary of preprocessor cubes and paths. """ preproc, preproc_path = get_preproc_lists(preproc_vars) @@ -185,14 +204,17 @@ def get_looping_dict(preproc_vars: list): def load_wt_files(path: str, mode="slwt"): """Load wt files. - Args: - ---- - path (str): Path of wt file - mode (str, optional): Type of weathertype to load. Defaults to "slwt". + Parameters + ---------- + path + Path of wt file + mode + Type of weathertype to load. Defaults to "slwt". Returns ------- - list: List of weathertype cubes. + list + List of weathertype cubes. """ if mode == "slwt": lwt_cube = iris.load_cube(path, "lwt") @@ -218,17 +240,23 @@ def get_provenance_record( ) -> dict: """Get provenance record. - Args: - ---- - caption (str): Caption of plot - ancestors (list): List of ancestor plots - long_names (list): List of variable long names - plot_types (list | None): Type of plot - statistics (list | None): Types of statistics used + Parameters + ---------- + caption + Caption of plot + ancestors + List of ancestor plots + long_names + List of variable long names + plot_types + Type of plot + statistics + Types of statistics used Returns ------- - dict: Provenance record + dict + Provenance record """ record = { "caption": caption, @@ -249,11 +277,14 @@ def get_provenance_record( def log_provenance(filename: str, cfg: dict, provenance_record: dict): """Log provenance. - Args: - ---- - filename (str): Filename of xml file - cfg (dict): Configuration dictionary - provenance_record (dict): Provenance record dictionary + Parameters + ---------- + filename: + Filename of xml file + cfg + Configuration dictionary + provenance_record + Provenance record dictionary """ with ProvenanceLogger(cfg) as provenance_logger: provenance_logger.log(filename, provenance_record) @@ -267,13 +298,15 @@ def log_provenance(filename: str, cfg: dict, provenance_record: dict): def turn_list_to_mapping_dict(list_: list) -> dict: """Turn list of combined WT to a dictionary for further use. - Args: - ---- - list_ (list): List of combined WTs + Parameters + ---------- + list_ + List of combined WTs Returns ------- - dict: Dictionary of combined WTs + dict + Dictionary of combined WTs """ result_dict = {} @@ -290,13 +323,15 @@ def turn_list_to_mapping_dict(list_: list) -> dict: def get_mapping_dict(selected_pairs: list) -> dict: """Get mapping dictionary from list of selected pairs. - Args: - ---- - selected_pairs (list): List of selected weathertype pairs + Parameters + ---------- + selected_pairs + List of selected weathertype pairs Returns ------- - dict: Mapping dictionary + dict + Mapping dictionary """ mapping_array = [elem[0] for elem in selected_pairs] @@ -318,11 +353,14 @@ def find_intersection(m_list: list) -> list: def write_mapping_dict(work_dir: str, dataset: str, mapping_dict: dict): """Write mapping dictionary to file. - Args: - ---- - work_dir (str): Current working directory - dataset (str): Dataset name - mapping_dict (dict): Mapping dictionary in {lwt: slwt, ...} format + Parameters + ---------- + work_dir + Current working directory + dataset + Dataset name + mapping_dict + Mapping dictionary in {lwt: slwt, ...} format """ mapping_dict_reformat = convert_dict(mapping_dict) @@ -335,13 +373,15 @@ def write_mapping_dict(work_dir: str, dataset: str, mapping_dict: dict): def convert_dict(dict_: dict) -> dict: """Convert dictionary from {lwt: slwt, ...} format to {slwt: [lwt1, lwt2], ...}. - Args: - ---- - dict_ (dict): Mapping dictionary to be converted + Parameters + ---------- + dict_ + Mapping dictionary to be converted Returns ------- - dict: Converted dictionary + dict + Converted dictionary """ new_dict = {} for dataset, value in dict_.items(): @@ -351,20 +391,23 @@ def convert_dict(dict_: dict) -> dict: return new_dict -def reverse_convert_dict(dict_: dict) -> dict: +def reverse_convert_dict(originial_dict: dict) -> dict: """Convert mapping dictionary. From {slwt: [lwt1, lwt2], ...} format to {lwt: slwt, ...}. - Args: - original_dict (dict): Dict in the {slwt: [lwt1, lwt2], ...} format + Parameters + ---------- + original_dict + Dict in the {slwt: [lwt1, lwt2], ...} format Returns ------- - dict: Dict in the format {lwt: slwt, ...} + dict + Dict in the format {lwt: slwt, ...} """ new_dict = {} - for key, value_list in dict_.items(): + for key, value_list in originial_dict.items(): for original_key in value_list: new_dict[original_key] = key return new_dict @@ -378,12 +421,16 @@ def write_corr_rmse_to_csv( ): """Write correlation and rsme matrix to csv files. - Args: - ---- - cfg (dict): Configuration dictionary from recipe - pattern_correlation_matrix (np.array): Correlation matrix - rmse_matrix (np.array): RSME matrix - dataset (str): Name of dataset + Parameters + ---------- + cfg + Configuration dictionary from recipe + pattern_correlation_matrix + Correlation matrix + rmse_matrix + RSME matrix + dataset + Name of dataset """ logger.info("Writing corr and rsme matrices for %s", dataset) @@ -414,17 +461,22 @@ def run_predefined_slwt( ): """Run predefined slwt mapping. - Args: - ---- - work_dir (str): Working directory to save mapping dict - dataset_name (str): Name of dataset - lwt (np.array): lwt array - predefined_slwt (dict): Mapping dictionary in {lwt: slwt, ...} format - + Parameters + ---------- + work_dir + Working directory to save mapping dict + dataset_name + Name of dataset + lwt + lwt array + predefined_slwt + Mapping dictionary in {lwt: slwt, ...} format Returns ------- - np.array: slwt_era5 array - np.array: slwt_eobs array + np.array + slwt_era5 array + np.array + slwt_eobs array """ predefined_slwt = check_mapping_dict_format(predefined_slwt) write_mapping_dict(work_dir, dataset_name, predefined_slwt) @@ -443,12 +495,16 @@ def combine_wt_to_file( ): """Combine lwt and slwt arrays to one file. - Args: - ---- - cfg (dict): Configuration dictionary from recipe - wt_list (list): List of weathertype arrays - cube (iris.cube.Cube): Cube of data to keep time coordinate - file_name (str): Name of output file + Parameters + ---------- + cfg + Configuration dictionary from recipe + wt_list + List of weathertype arrays + cube + Cube of data to keep time coordinate + file_name + Name of output file """ lwt = wt_list[0] slwt_era5 = wt_list[1] @@ -491,12 +547,16 @@ def write_lwt_to_file( ): """Write only lwt to file. - Args: - ---- - cfg (dict): Configuration dictionary from recipe - lwt (np.array): lwt array - cube (iris.cube.Cube): Cube of data to keep time coordinate - file_name (str): Name of output file + Parameters + ---------- + cfg + Configuration dictionary from recipe + lwt + lwt array + cube + Cube of data to keep time coordinate + file_name + Name of output file """ logger.info("Writing Lamb Weathertype to %s", file_name) @@ -520,14 +580,17 @@ def write_lwt_to_file( def map_lwt_to_slwt(lwt: np.array, mapping_dict: dict) -> np.array: """Map lwt array to slwt array. - Args: - ---- - lwt (np.array): lwt array - mapping_dict (dict): Mapping dictionary in {lwt: slwt, ...} format + Parameters + ---------- + lwt + lwt array + mapping_dict + Mapping dictionary in {lwt: slwt, ...} format Returns ------- - np.array: array of slwt + np.array + array of slwt """ return np.array([np.int8(mapping_dict.get(value, 0)) for value in lwt]) @@ -535,13 +598,15 @@ def map_lwt_to_slwt(lwt: np.array, mapping_dict: dict) -> np.array: def check_mapping_dict_format(mapping_dict: dict) -> dict: """Check format of mapping dict and return in {lwt: slwt, ...} format. - Args: - ---- - mapping_dict (dict): mapping dict in any format + Parameters + ---------- + mapping_dict + mapping dict in any format Returns ------- - dict: mapping dict in {lwt: slwt, ...} format + dict + mapping dict in {lwt: slwt, ...} format """ if isinstance(mapping_dict[next(iter(mapping_dict))], list): return reverse_convert_dict(mapping_dict) From 81ec1bda6bd794220b9c992cf7d8c803a852a3b1 Mon Sep 17 00:00:00 2001 From: Thomas Kroissenbrunner Date: Tue, 9 Dec 2025 09:06:28 +0100 Subject: [PATCH 32/57] add blank lines --- esmvaltool/diag_scripts/weathertyping/calc_utils.py | 1 + esmvaltool/diag_scripts/weathertyping/plot_utils.py | 1 + esmvaltool/diag_scripts/weathertyping/wt_utils.py | 4 +++- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/esmvaltool/diag_scripts/weathertyping/calc_utils.py b/esmvaltool/diag_scripts/weathertyping/calc_utils.py index 21a645f2d9..4a6555ddb5 100644 --- a/esmvaltool/diag_scripts/weathertyping/calc_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/calc_utils.py @@ -675,6 +675,7 @@ def process_prcp_mean( Name of dataset timerange Time range of dataset + Returns ------- list diff --git a/esmvaltool/diag_scripts/weathertyping/plot_utils.py b/esmvaltool/diag_scripts/weathertyping/plot_utils.py index bb629ab9e1..949431a9a9 100644 --- a/esmvaltool/diag_scripts/weathertyping/plot_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/plot_utils.py @@ -208,6 +208,7 @@ def prepare_plot_title( name of variable mode statistic used + Returns ------- str diff --git a/esmvaltool/diag_scripts/weathertyping/wt_utils.py b/esmvaltool/diag_scripts/weathertyping/wt_utils.py index cb74c64cbc..1f46f16d10 100644 --- a/esmvaltool/diag_scripts/weathertyping/wt_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/wt_utils.py @@ -72,7 +72,8 @@ def load_wt_preprocessors(dataset: str, preproc_variables_dict: dict) -> tuple: def get_ancestors_era5_eobs( - dataset: str, preproc_variables_dict: dict + dataset: str, + preproc_variables_dict: dict, ) -> tuple: """Get ancestors for observational data. @@ -471,6 +472,7 @@ def run_predefined_slwt( lwt array predefined_slwt Mapping dictionary in {lwt: slwt, ...} format + Returns ------- np.array From 18e109c61c6e720aeeca53d117611fb3507da685 Mon Sep 17 00:00:00 2001 From: Thomas Kroissenbrunner Date: Thu, 11 Dec 2025 09:02:08 +0100 Subject: [PATCH 33/57] latest review fixes/changes --- .../diag_scripts/weathertyping/calc_utils.py | 35 ++++++++------ .../weathertyping/weathertyping.py | 4 +- .../diag_scripts/weathertyping/wt_utils.py | 48 +++++++++++++------ 3 files changed, 55 insertions(+), 32 deletions(-) diff --git a/esmvaltool/diag_scripts/weathertyping/calc_utils.py b/esmvaltool/diag_scripts/weathertyping/calc_utils.py index 4a6555ddb5..e77369f2c7 100644 --- a/esmvaltool/diag_scripts/weathertyping/calc_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/calc_utils.py @@ -6,6 +6,7 @@ from pathlib import Path from typing import Final +import dask.array as da import iris import iris.analysis.cartography import numpy as np @@ -80,8 +81,8 @@ def calc_slwt_obs( wt_data_prcp = [] for wt_ in range(1, 28): - target_indices = np.where(lwt == wt_) - if len(target_indices[0]) < 1: + target_indices = da.where(lwt == wt_).compute() + if not target_indices[0]: logger.info( "calc_slwt_obs - CAUTION: Skipped wt %s \ for dataset %s!", @@ -100,7 +101,15 @@ def calc_slwt_obs( iris.Constraint(time=lambda t, d=dates: t.point in d[0]), ) wt_cube_mean = extracted_cube.collapsed("time", iris.analysis.MEAN) - wt_data_prcp.append(wt_cube_mean.data.compressed()) + + data = wt_cube_mean.data + + if isinstance(data, np.ma.MaskedArray): + wt_data_prcp.append(data.compressed()) + else: + # if not MaskedArray, flatten should be fine + wt_data_prcp.append(data.flatten()) + selected_pairs = process_prcp_mean( cfg, wt_data_prcp, @@ -372,7 +381,7 @@ def lamp_synoptic_directional_type( weathertypes: np.array, i: int, ) -> int: - """Calculate Lamp synoptic/directional hybrid weathertype. + """Calculate Lamb synoptic/directional hybrid weathertype. Parameters ---------- @@ -696,11 +705,7 @@ def process_prcp_mean( "correlation_threshold", ) and rmse_matrix[i][j] <= cfg.get("rmse_threshold"): selected_pairs.append( - ( - (i + 1, j + 1), - pattern_correlation_matrix[i][j], - rmse_matrix[i][j], - ), + ((i + 1, j + 1),), ) # write matrices to csv @@ -755,7 +760,7 @@ def calc_wt_means( wt_array, tcoord = get_wt_array(wt_string, wt_cubes) for wt in range(1, len(np.unique(wt_array)) + 1): - target_indices = np.where(wt_array == wt) + target_indices = da.where(wt_array == wt).compute() if len(target_indices[0]) < 1: logger.info( "calc_wt_means - CAUTION: Skipped %s %s \ @@ -835,7 +840,7 @@ def get_wt_array(wt_string: str, wt_cubes: iris.cube.CubeList) -> tuple: error_str, ) - return wt_array, tcoord + return wt_array.core_data(), tcoord def calc_wt_anomalies( @@ -870,8 +875,8 @@ def calc_wt_anomalies( wt_array, tcoord = get_wt_array(wt_string, wt_cubes) for wt in range(1, len(np.unique(wt_array)) + 1): - target_indices = np.where(wt_array == wt) - if len(target_indices[0]) < 1: + target_indices = da.where(wt_array == wt).compute() + if not target_indices[0]: logger.info( "calc_wt_anomalies - CAUTION: Skipped wt %s \ for dataset %s!", @@ -947,8 +952,8 @@ def calc_wt_std( wt_array, tcoord = get_wt_array(wt_string, wt_cubes) for wt in range(1, len(np.unique(wt_array)) + 1): - target_indices = np.where(wt_array == wt) - if len(target_indices[0]) < 1: + target_indices = da.where(wt_array == wt).compute() + if not target_indices[0]: logger.info( "calc_slwt_obs - CAUTION: Skipped wt %s \ for dataset %s!", diff --git a/esmvaltool/diag_scripts/weathertyping/weathertyping.py b/esmvaltool/diag_scripts/weathertyping/weathertyping.py index c535a21849..50fd47d42d 100644 --- a/esmvaltool/diag_scripts/weathertyping/weathertyping.py +++ b/esmvaltool/diag_scripts/weathertyping/weathertyping.py @@ -368,7 +368,7 @@ def run_lwt(cfg: dict): process_models_lwt(cfg, dataset_vars, data_info) -def run_my_diagnostic(cfg: dict): +def run_weathertyping_diagnostic(cfg: dict): """Run the weathertyping diagnostic. Parameters @@ -392,4 +392,4 @@ def run_my_diagnostic(cfg: dict): if __name__ == "__main__": with run_diagnostic() as config: # main function for running the diagnostic - run_my_diagnostic(config) + run_weathertyping_diagnostic(config) diff --git a/esmvaltool/diag_scripts/weathertyping/wt_utils.py b/esmvaltool/diag_scripts/weathertyping/wt_utils.py index 1f46f16d10..590fe5b794 100644 --- a/esmvaltool/diag_scripts/weathertyping/wt_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/wt_utils.py @@ -64,7 +64,9 @@ def load_wt_preprocessors(dataset: str, preproc_variables_dict: dict) -> tuple: preproc_variables_dict.get("E-OBS")[0].get("filename"), ) except (IndexError, KeyError, TypeError): - print("ERA5 precipitation preprocessor not found for automatic slwt.") + logger.info( + "ERA5 precipitation preprocessor not found for automatic slwt." + ) wt_preproc_prcp = None wt_preproc_prcp_eobs = None @@ -101,7 +103,9 @@ def get_ancestors_era5_eobs( preproc_variables_dict.get("E-OBS")[0].get("filename"), ] except (IndexError, KeyError, TypeError): - print("No ancestors for ERA5 and E-OBS precipitation preprocessor.") + logger.info( + "No ancestors for ERA5 and E-OBS precipitation preprocessor." + ) eobs_ancestors = [] return era5_ancestors, eobs_ancestors @@ -315,8 +319,6 @@ def turn_list_to_mapping_dict(list_: list) -> dict: for elem in s: if elem not in result_dict: result_dict[elem] = i + 1 - else: - result_dict[elem].append(i) return result_dict @@ -334,21 +336,37 @@ def get_mapping_dict(selected_pairs: list) -> dict: dict Mapping dictionary """ - mapping_array = [elem[0] for elem in selected_pairs] - s = [set(i) for i in mapping_array if i] + # selected pairs is of form [(wt1, wt2), (wt3, wt4), ...] + sets_list = [set(i) for i in selected_pairs if i] + + # 3 loops to check all sets against each other until no more merges happen + # merged_flag indicates if a merge happened in the last full pass -> we need to restart then + merged_flag = True + + while merged_flag: + merged_flag = False + i = 0 + + # check each set against each other + while i < len(sets_list): + set1 = sets_list[i] + j = i + 1 + + while j < len(sets_list): + set2 = sets_list[j] - def find_intersection(m_list: list) -> list: - for i, v in enumerate(m_list): - for j, k in enumerate(m_list[i + 1 :], i + 1): - if v & k: - s[i] = v.union(m_list.pop(j)) - return find_intersection(m_list) - return m_list + if set1.isdisjoint(set2) is False: + # this means there is an overlap -> merge sets + set1.update(set2) + sets_list.pop(j) + merged_flag = True + else: + j += 1 - merged_tuples = find_intersection(s) + i += 1 - return turn_list_to_mapping_dict(merged_tuples) + return turn_list_to_mapping_dict(sets_list) def write_mapping_dict(work_dir: str, dataset: str, mapping_dict: dict): From 1335bdc6c6f749fb4f153a76802fa1233a1597a0 Mon Sep 17 00:00:00 2001 From: Thomas Kroissenbrunner Date: Thu, 11 Dec 2025 09:05:43 +0100 Subject: [PATCH 34/57] trailing comma --- esmvaltool/diag_scripts/weathertyping/wt_utils.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/esmvaltool/diag_scripts/weathertyping/wt_utils.py b/esmvaltool/diag_scripts/weathertyping/wt_utils.py index 590fe5b794..7a83ec091d 100644 --- a/esmvaltool/diag_scripts/weathertyping/wt_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/wt_utils.py @@ -65,7 +65,7 @@ def load_wt_preprocessors(dataset: str, preproc_variables_dict: dict) -> tuple: ) except (IndexError, KeyError, TypeError): logger.info( - "ERA5 precipitation preprocessor not found for automatic slwt." + "ERA5 precipitation preprocessor not found for automatic slwt.", ) wt_preproc_prcp = None wt_preproc_prcp_eobs = None @@ -104,7 +104,7 @@ def get_ancestors_era5_eobs( ] except (IndexError, KeyError, TypeError): logger.info( - "No ancestors for ERA5 and E-OBS precipitation preprocessor." + "No ancestors for ERA5 and E-OBS precipitation preprocessor.", ) eobs_ancestors = [] @@ -336,7 +336,6 @@ def get_mapping_dict(selected_pairs: list) -> dict: dict Mapping dictionary """ - # selected pairs is of form [(wt1, wt2), (wt3, wt4), ...] sets_list = [set(i) for i in selected_pairs if i] From d91df5057f2c1f886eb23c5907ca0fdaea632199 Mon Sep 17 00:00:00 2001 From: thomaskroi1996 <131807984+thomaskroi1996@users.noreply.github.com> Date: Wed, 17 Dec 2025 11:27:36 +0100 Subject: [PATCH 35/57] Update esmvaltool/references/maraun21jgr.bibtex Co-authored-by: Bettina Gier --- esmvaltool/references/maraun21jgr.bibtex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esmvaltool/references/maraun21jgr.bibtex b/esmvaltool/references/maraun21jgr.bibtex index 613e5d1bb9..d160c9e78f 100644 --- a/esmvaltool/references/maraun21jgr.bibtex +++ b/esmvaltool/references/maraun21jgr.bibtex @@ -1,4 +1,4 @@ -@article{https://doi.org/10.1029/2020JD032824, +@article{maraun21jgr, author = {Maraun, Douglas and Truhetz, Heimo and Schaffer, Armin}, title = {Regional Climate Model Biases, Their Dependence on Synoptic Circulation Biases and the Potential for Bias Adjustment: A Process-Oriented Evaluation of the Austrian Regional Climate Projections}, journal = {Journal of Geophysical Research: Atmospheres}, From dc7e954a8887deab129009531672d1231f45abf4 Mon Sep 17 00:00:00 2001 From: thomaskroi1996 <131807984+thomaskroi1996@users.noreply.github.com> Date: Wed, 17 Dec 2025 11:28:08 +0100 Subject: [PATCH 36/57] Update doc/sphinx/source/recipes/recipe_weathertyping.rst Co-authored-by: Bettina Gier --- doc/sphinx/source/recipes/recipe_weathertyping.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/sphinx/source/recipes/recipe_weathertyping.rst b/doc/sphinx/source/recipes/recipe_weathertyping.rst index e36f2afff6..80c2f73684 100755 --- a/doc/sphinx/source/recipes/recipe_weathertyping.rst +++ b/doc/sphinx/source/recipes/recipe_weathertyping.rst @@ -111,4 +111,4 @@ Example plots .. figure:: /recipes/figures/weathertyping/ERA5__lwt_rel_occurrence_1958-2014.png :align: center - Stackplot of seasonal relative occureences of each WT for ERA5. + Stackplot of seasonal relative occurrences of each WT for ERA5. From 70d34e43874375186a4e77ca594ca08e5a1d5557 Mon Sep 17 00:00:00 2001 From: thomaskroi1996 <131807984+thomaskroi1996@users.noreply.github.com> Date: Wed, 17 Dec 2025 11:28:30 +0100 Subject: [PATCH 37/57] Update esmvaltool/config-references.yml Co-authored-by: Bettina Gier --- esmvaltool/config-references.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/esmvaltool/config-references.yml b/esmvaltool/config-references.yml index 1710804fcf..2e9586c21f 100644 --- a/esmvaltool/config-references.yml +++ b/esmvaltool/config-references.yml @@ -363,7 +363,6 @@ authors: kroissenbrunner_thomas: name: Kroissenbrunner, Thomas institute: WEGC, Austria - email: thomas.kroissenbrunner@uni-graz.at github: thomaskroi1996 orcid: kuehbacher_birgit: From f46b7f634f19baf46683307f4d49d1a440c5be59 Mon Sep 17 00:00:00 2001 From: thomaskroi1996 <131807984+thomaskroi1996@users.noreply.github.com> Date: Wed, 17 Dec 2025 11:28:48 +0100 Subject: [PATCH 38/57] Update esmvaltool/references/jones93ijc.bibtex Co-authored-by: Bettina Gier --- esmvaltool/references/jones93ijc.bibtex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esmvaltool/references/jones93ijc.bibtex b/esmvaltool/references/jones93ijc.bibtex index 360850ffdf..c3f0ee590d 100644 --- a/esmvaltool/references/jones93ijc.bibtex +++ b/esmvaltool/references/jones93ijc.bibtex @@ -1,4 +1,4 @@ -@article{https://doi.org/10.1002/joc.3370130606, +@article{jones93ijc, author = {Jones, P. D. and Hulme, M. and Briffa, K. R.}, title = {A comparison of Lamb circulation types with an objective classification scheme}, journal = {International Journal of Climatology}, From 50006331ab787b70e9cb1522e4278fc89e49e4fd Mon Sep 17 00:00:00 2001 From: thomaskroi1996 <131807984+thomaskroi1996@users.noreply.github.com> Date: Wed, 17 Dec 2025 11:29:10 +0100 Subject: [PATCH 39/57] Update esmvaltool/recipes/recipe_weathertyping_CMIP6.yml Co-authored-by: Bettina Gier --- esmvaltool/recipes/recipe_weathertyping_CMIP6.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/esmvaltool/recipes/recipe_weathertyping_CMIP6.yml b/esmvaltool/recipes/recipe_weathertyping_CMIP6.yml index 737a00e16b..f7977f42a7 100644 --- a/esmvaltool/recipes/recipe_weathertyping_CMIP6.yml +++ b/esmvaltool/recipes/recipe_weathertyping_CMIP6.yml @@ -62,7 +62,6 @@ datasets_psl_CMIP6: &datasets_psl_cmip6 - {institute: NCC, dataset: NorCPM1, ensemble: r1i1p1f1, grid: gn, esgf_version: v20200724} - {institute: NCC, dataset: NorESM2-LM, ensemble: r1i1p1f1, grid: gn, esgf_version: v20190815} - {institute: NCC, dataset: NorESM2-MM, ensemble: r1i1p1f1, grid: gn, esgf_version: v20191108} - # - {institute: NIMS-KMA, dataset: KACE-1-0-G, ensemble: r1i1p1f1, grid: gr, esgf_version: v20190911} #discontinous longitude coordinate warning - {institute: NOAA-GFDL, dataset: GFDL-CM4, ensemble: r1i1p1f1, grid: gr1, esgf_version: v20180701} - {institute: NOAA-GFDL, dataset: GFDL-CM4, ensemble: r1i1p1f1, grid: gr2, esgf_version: v20180701} - {institute: NOAA-GFDL, dataset: GFDL-ESM4, ensemble: r1i1p1f1, grid: gr1, esgf_version: v20190726} From 7e06bc44982aa457ba87912a029a7b4d71f6c39e Mon Sep 17 00:00:00 2001 From: thomaskroi1996 <131807984+thomaskroi1996@users.noreply.github.com> Date: Wed, 17 Dec 2025 11:29:24 +0100 Subject: [PATCH 40/57] Update esmvaltool/recipes/recipe_weathertyping_CMIP6.yml Co-authored-by: Bettina Gier --- esmvaltool/recipes/recipe_weathertyping_CMIP6.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/esmvaltool/recipes/recipe_weathertyping_CMIP6.yml b/esmvaltool/recipes/recipe_weathertyping_CMIP6.yml index f7977f42a7..ee4b00a01f 100644 --- a/esmvaltool/recipes/recipe_weathertyping_CMIP6.yml +++ b/esmvaltool/recipes/recipe_weathertyping_CMIP6.yml @@ -57,7 +57,6 @@ datasets_psl_CMIP6: &datasets_psl_cmip6 - {institute: NASA-GISS, dataset: GISS-E2-2-G, ensemble: r1i1p1f1, grid: gn, esgf_version: v20191120, start_year: 1970} - {institute: NCAR, dataset: CESM2-FV2, ensemble: r1i1p1f1, grid: gn, esgf_version: v20191120} - {institute: NCAR, dataset: CESM2-WACCM-FV2, ensemble: r1i1p1f1, grid: gn, esgf_version: v20191120} - # - {institute: NCAR, dataset: CESM2-WACCM, ensemble: r1i1p1f1, grid: gn, esgf_version: v20190227} #concatenate error - {institute: NCAR, dataset: CESM2, ensemble: r1i1p1f1, grid: gn, esgf_version: v20190308} - {institute: NCC, dataset: NorCPM1, ensemble: r1i1p1f1, grid: gn, esgf_version: v20200724} - {institute: NCC, dataset: NorESM2-LM, ensemble: r1i1p1f1, grid: gn, esgf_version: v20190815} From 1c0d8a6ac13b1871c1cceaf97ecf3f889f2e365f Mon Sep 17 00:00:00 2001 From: thomaskroi1996 <131807984+thomaskroi1996@users.noreply.github.com> Date: Wed, 17 Dec 2025 11:30:29 +0100 Subject: [PATCH 41/57] Update esmvaltool/config-references.yml Co-authored-by: Bettina Gier --- esmvaltool/config-references.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/esmvaltool/config-references.yml b/esmvaltool/config-references.yml index 2e9586c21f..939696123f 100644 --- a/esmvaltool/config-references.yml +++ b/esmvaltool/config-references.yml @@ -325,7 +325,6 @@ authors: jury_martin: name: Jury, Martin institute: WEGC, Austria - e-mail: martin.jury@uni-graz.at orcid: https://orcid.org/0000-0003-0590-7843 kadygrov_nikolay: name: Kadygrov, Nikolay From 23eccebc4a80bfd62e6c8f5341ebcc59f911660d Mon Sep 17 00:00:00 2001 From: thomaskroi1996 <131807984+thomaskroi1996@users.noreply.github.com> Date: Wed, 17 Dec 2025 11:30:55 +0100 Subject: [PATCH 42/57] Update esmvaltool/diag_scripts/weathertyping/plot_utils.py Co-authored-by: Bettina Gier --- esmvaltool/diag_scripts/weathertyping/plot_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esmvaltool/diag_scripts/weathertyping/plot_utils.py b/esmvaltool/diag_scripts/weathertyping/plot_utils.py index 949431a9a9..8605fb6a70 100644 --- a/esmvaltool/diag_scripts/weathertyping/plot_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/plot_utils.py @@ -26,7 +26,7 @@ def generate_grayscale_hex_values(x): - """Generate grayscale values for plotting seasonal occurences. + """Generate grayscale values for plotting seasonal occurrences. Parameters ---------- From fca1031b1989360be67c5c19f8f41afc0935c3eb Mon Sep 17 00:00:00 2001 From: thomaskroi1996 <131807984+thomaskroi1996@users.noreply.github.com> Date: Wed, 17 Dec 2025 11:31:16 +0100 Subject: [PATCH 43/57] Update esmvaltool/diag_scripts/weathertyping/plot_utils.py Co-authored-by: Bettina Gier --- esmvaltool/diag_scripts/weathertyping/plot_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esmvaltool/diag_scripts/weathertyping/plot_utils.py b/esmvaltool/diag_scripts/weathertyping/plot_utils.py index 8605fb6a70..093c01521b 100644 --- a/esmvaltool/diag_scripts/weathertyping/plot_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/plot_utils.py @@ -51,7 +51,7 @@ def plot_seasonal_occurrence( wt_cubes: iris.cube.Cube, data_info: dict, ): - """Plot seasonal occurences of weathertypes. + """Plot seasonal occurrences of weathertypes. Parameters ---------- From 242d774607e1b637ffcf51da310eab2ffe84c33a Mon Sep 17 00:00:00 2001 From: thomaskroi1996 <131807984+thomaskroi1996@users.noreply.github.com> Date: Wed, 17 Dec 2025 11:31:43 +0100 Subject: [PATCH 44/57] Update esmvaltool/diag_scripts/weathertyping/plot_utils.py Co-authored-by: Bettina Gier --- esmvaltool/diag_scripts/weathertyping/plot_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esmvaltool/diag_scripts/weathertyping/plot_utils.py b/esmvaltool/diag_scripts/weathertyping/plot_utils.py index 093c01521b..2334596877 100644 --- a/esmvaltool/diag_scripts/weathertyping/plot_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/plot_utils.py @@ -115,7 +115,7 @@ def plot_seasonal_occurrence( _, ax_ = plt.subplots(figsize=(10, 10)) ax_.set_title( - f"Seasonal occurence of {wt_string}, \ + f"Seasonal occurrence of {wt_string}, \ {data_info.get('dataset')}, {data_info.get('timerange')}", ) From 302f1a777a1d1bea287e193e460b9a35290f3650 Mon Sep 17 00:00:00 2001 From: thomaskroi1996 <131807984+thomaskroi1996@users.noreply.github.com> Date: Wed, 17 Dec 2025 11:32:06 +0100 Subject: [PATCH 45/57] Update esmvaltool/diag_scripts/weathertyping/wt_utils.py Co-authored-by: Bettina Gier --- esmvaltool/diag_scripts/weathertyping/wt_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esmvaltool/diag_scripts/weathertyping/wt_utils.py b/esmvaltool/diag_scripts/weathertyping/wt_utils.py index 7a83ec091d..1c0e909592 100644 --- a/esmvaltool/diag_scripts/weathertyping/wt_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/wt_utils.py @@ -437,7 +437,7 @@ def write_corr_rmse_to_csv( rmse_matrix: np.array, dataset: str, ): - """Write correlation and rsme matrix to csv files. + """Write correlation and rmse matrix to csv files. Parameters ---------- From f5b39359c5c51a4b62c5d61a5a2a6ccfa175115a Mon Sep 17 00:00:00 2001 From: thomaskroi1996 <131807984+thomaskroi1996@users.noreply.github.com> Date: Wed, 17 Dec 2025 11:32:35 +0100 Subject: [PATCH 46/57] Update esmvaltool/diag_scripts/weathertyping/wt_utils.py Co-authored-by: Bettina Gier --- esmvaltool/diag_scripts/weathertyping/wt_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esmvaltool/diag_scripts/weathertyping/wt_utils.py b/esmvaltool/diag_scripts/weathertyping/wt_utils.py index 1c0e909592..4f5a8e99ca 100644 --- a/esmvaltool/diag_scripts/weathertyping/wt_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/wt_utils.py @@ -446,7 +446,7 @@ def write_corr_rmse_to_csv( pattern_correlation_matrix Correlation matrix rmse_matrix - RSME matrix + RMSE matrix dataset Name of dataset """ From f2579c34dfad429f3f5330c01a56f02923619eb8 Mon Sep 17 00:00:00 2001 From: thomaskroi1996 <131807984+thomaskroi1996@users.noreply.github.com> Date: Wed, 17 Dec 2025 11:32:52 +0100 Subject: [PATCH 47/57] Update esmvaltool/recipes/recipe_weathertyping_CMIP6.yml Co-authored-by: Bettina Gier --- esmvaltool/recipes/recipe_weathertyping_CMIP6.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/esmvaltool/recipes/recipe_weathertyping_CMIP6.yml b/esmvaltool/recipes/recipe_weathertyping_CMIP6.yml index ee4b00a01f..484d6e7380 100644 --- a/esmvaltool/recipes/recipe_weathertyping_CMIP6.yml +++ b/esmvaltool/recipes/recipe_weathertyping_CMIP6.yml @@ -28,7 +28,6 @@ datasets_psl_CMIP6: &datasets_psl_cmip6 - {institute: BCC, dataset: BCC-ESM1, ensemble: r1i1p1f1, grid: gn, esgf_version: v20181220} - {institute: CAS, dataset: FGOALS-f3-L, ensemble: r1i1p1f1, grid: gr, esgf_version: v20191019} - {institute: CAS, dataset: FGOALS-g3, ensemble: r1i1p1f1, grid: gn, esgf_version: v20190826} - # - {institute: CCCR-IITM, dataset: IITM-ESM, ensemble: r1i1p1f1, grid: gn, esgf_version: v20210203} #discontinous longitude warning - {institute: CCCma, dataset: CanESM5, ensemble: r1i1p1f1, grid: gn, esgf_version: v20190429} - {institute: CMCC, dataset: CMCC-CM2-HR4, ensemble: r1i1p1f1, grid: gn, esgf_version: v20200904} - {institute: CMCC, dataset: CMCC-CM2-SR5, ensemble: r1i1p1f1, grid: gn, esgf_version: v20200616} From 835fcf11d94f8ccddf8b66801d9ce24c72cdbabb Mon Sep 17 00:00:00 2001 From: thomaskroi1996 <131807984+thomaskroi1996@users.noreply.github.com> Date: Wed, 17 Dec 2025 11:33:13 +0100 Subject: [PATCH 48/57] Update esmvaltool/recipes/recipe_weathertyping_CMIP6.yml Co-authored-by: Bettina Gier --- esmvaltool/recipes/recipe_weathertyping_CMIP6.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/esmvaltool/recipes/recipe_weathertyping_CMIP6.yml b/esmvaltool/recipes/recipe_weathertyping_CMIP6.yml index 484d6e7380..432e821ad2 100644 --- a/esmvaltool/recipes/recipe_weathertyping_CMIP6.yml +++ b/esmvaltool/recipes/recipe_weathertyping_CMIP6.yml @@ -44,7 +44,6 @@ datasets_psl_CMIP6: &datasets_psl_cmip6 - {institute: INM, dataset: INM-CM4-8, ensemble: r1i1p1f1, grid: gr1, esgf_version: v20190530} - {institute: INM, dataset: INM-CM5-0, ensemble: r1i1p1f1, grid: gr1, esgf_version: v20190610} - {institute: IPSL, dataset: IPSL-CM5A2-INCA, ensemble: r1i1p1f1, grid: gr, esgf_version: v20200729} - # - {institute: IPSL, dataset: IPSL-CM6A-LR-INCA, ensemble: r1i1p1f1, grid: gr, esgf_version: v20210216} #??? - {institute: IPSL, dataset: IPSL-CM6A-LR, ensemble: r1i1p1f1, grid: gr, esgf_version: v20180803} - {institute: KIOST, dataset: KIOST-ESM, ensemble: r1i1p1f1, grid: gr1, esgf_version: v20210601} - {institute: MIROC, dataset: MIROC-ES2L, ensemble: r1i1p1f2, grid: gn, esgf_version: v20191129} From 79844d388ff97a85fc7ed7ea9bfa3b8c5a5ab5e6 Mon Sep 17 00:00:00 2001 From: thomaskroi1996 <131807984+thomaskroi1996@users.noreply.github.com> Date: Wed, 17 Dec 2025 11:33:29 +0100 Subject: [PATCH 49/57] Update esmvaltool/recipes/recipe_weathertyping_CMIP6.yml Co-authored-by: Bettina Gier --- esmvaltool/recipes/recipe_weathertyping_CMIP6.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/esmvaltool/recipes/recipe_weathertyping_CMIP6.yml b/esmvaltool/recipes/recipe_weathertyping_CMIP6.yml index 432e821ad2..c745ff5b3e 100644 --- a/esmvaltool/recipes/recipe_weathertyping_CMIP6.yml +++ b/esmvaltool/recipes/recipe_weathertyping_CMIP6.yml @@ -48,7 +48,6 @@ datasets_psl_CMIP6: &datasets_psl_cmip6 - {institute: KIOST, dataset: KIOST-ESM, ensemble: r1i1p1f1, grid: gr1, esgf_version: v20210601} - {institute: MIROC, dataset: MIROC-ES2L, ensemble: r1i1p1f2, grid: gn, esgf_version: v20191129} - {institute: MIROC, dataset: MIROC6, ensemble: r1i1p1f1, grid: gn, esgf_version: v20191016} - # - {institute: MPI-M, dataset: ICON-ESM-LR, ensemble: r1i1p1f1, grid: gn, esgf_version: v20210215} #boundary values not 0 or 2 - {institute: MPI-M, dataset: MPI-ESM1-2-HR, ensemble: r1i1p1f1, grid: gn, esgf_version: v20190710} - {institute: MPI-M, dataset: MPI-ESM1-2-LR, ensemble: r1i1p1f1, grid: gn, esgf_version: v20190710} - {institute: MRI, dataset: MRI-ESM2-0, ensemble: r1i1p1f1, grid: gn, esgf_version: v20190603} From 46956efae10e7bbce711fb9d3cd15a9fb7fa1f25 Mon Sep 17 00:00:00 2001 From: Thomas Kroissenbrunner Date: Wed, 17 Dec 2025 15:44:16 +0100 Subject: [PATCH 50/57] bugfix! --- .../diag_scripts/weathertyping/calc_utils.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/esmvaltool/diag_scripts/weathertyping/calc_utils.py b/esmvaltool/diag_scripts/weathertyping/calc_utils.py index e77369f2c7..3e1c029d76 100644 --- a/esmvaltool/diag_scripts/weathertyping/calc_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/calc_utils.py @@ -758,10 +758,11 @@ def calc_wt_means( ) wt_array, tcoord = get_wt_array(wt_string, wt_cubes) + counts = da.bincount(wt_array.flatten().astype(int)).compute() - for wt in range(1, len(np.unique(wt_array)) + 1): - target_indices = da.where(wt_array == wt).compute() - if len(target_indices[0]) < 1: + for wt in range(1, len(counts)): + target_indices = da.where(wt_array == wt)[0].compute() + if len(target_indices) < 1: logger.info( "calc_wt_means - CAUTION: Skipped %s %s \ for dataset %s!", @@ -774,7 +775,7 @@ def calc_wt_means( tcoord.units.num2date(tcoord.points[i]) for i in target_indices ] extracted_cube = cube.extract( - iris.Constraint(time=lambda t, d=dates: t.point in d[0]), + iris.Constraint(time=lambda t, d=dates: t.point in d), ) wt_cube_mean = extracted_cube.collapsed("time", iris.analysis.MEAN) plot_maps(wt, cfg, wt_cube_mean, data_info, "mean") @@ -825,22 +826,22 @@ def get_wt_array(wt_string: str, wt_cubes: iris.cube.CubeList) -> tuple: if wt_string == "slwt_ERA5": slwt_era5_cube = wt_cubes[1] tcoord = slwt_era5_cube.coord("time") - wt_array = slwt_era5_cube.data[:] + wt_array = slwt_era5_cube.core_data()[:] elif wt_string == "slwt_EOBS": slwt_eobs_cube = wt_cubes[2] tcoord = slwt_eobs_cube.coord("time") - wt_array = slwt_eobs_cube.data[:] + wt_array = slwt_eobs_cube.core_data()[:] elif wt_string == "lwt": lwt_cube = wt_cubes[0] tcoord = lwt_cube.coord("time") - wt_array = lwt_cube.data[:] + wt_array = lwt_cube.core_data()[:] else: error_str = "wt_array does not exist for calc_wt_means, calc_wt_anomalies or calc_wt_std." raise NameError( error_str, ) - return wt_array.core_data(), tcoord + return wt_array, tcoord def calc_wt_anomalies( From ea6a27bae8f34373e6b1eb74fc96d35ab4266c01 Mon Sep 17 00:00:00 2001 From: Thomas Kroissenbrunner Date: Wed, 17 Dec 2025 15:58:58 +0100 Subject: [PATCH 51/57] added info about OBS --- doc/sphinx/source/recipes/recipe_weathertyping.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/sphinx/source/recipes/recipe_weathertyping.rst b/doc/sphinx/source/recipes/recipe_weathertyping.rst index 80c2f73684..f7fab453c2 100755 --- a/doc/sphinx/source/recipes/recipe_weathertyping.rst +++ b/doc/sphinx/source/recipes/recipe_weathertyping.rst @@ -70,6 +70,8 @@ Observations and reformat scripts (2) see headers of reformat scripts for non-obs4MIPs data for download instructions.* +This recipe currently only works with the following reanalysis and observation datasets: + * E-OBS: European Climate Assessment & Dataset gridded daily precipitation sum * ERA5: ECMWF reanalysis From fd32c2f317028ff324cc5d887705ed3db7e39dc4 Mon Sep 17 00:00:00 2001 From: Thomas Kroissenbrunner Date: Thu, 18 Dec 2025 07:55:38 +0100 Subject: [PATCH 52/57] include provenance for seasonal occurrence --- .../diag_scripts/weathertyping/calc_utils.py | 114 +++++++++--------- .../diag_scripts/weathertyping/plot_utils.py | 21 ++++ 2 files changed, 78 insertions(+), 57 deletions(-) diff --git a/esmvaltool/diag_scripts/weathertyping/calc_utils.py b/esmvaltool/diag_scripts/weathertyping/calc_utils.py index 3e1c029d76..5870c098bb 100644 --- a/esmvaltool/diag_scripts/weathertyping/calc_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/calc_utils.py @@ -780,27 +780,27 @@ def calc_wt_means( wt_cube_mean = extracted_cube.collapsed("time", iris.analysis.MEAN) plot_maps(wt, cfg, wt_cube_mean, data_info, "mean") - ancestors = [ - f"{data_info.get('preproc_path')}", - f"{cfg.get('work_dir')}/ERA5.nc", - ] - provenance_record = get_provenance_record( - f"{var_name} means for {wt_string}", - ancestors, - [var_name], - ["map"], - ["mean"], - ) + ancestors = [ + f"{data_info.get('preproc_path')}", + f"{cfg.get('work_dir')}/ERA5.nc", + ] + provenance_record = get_provenance_record( + f"{var_name} means for {wt_string}, wt: {wt}, ", + ancestors, + [var_name], + ["map"], + ["mean"], + ) - local_path = f"{cfg.get('plot_dir')}/mean" + local_path = f"{cfg.get('plot_dir')}/mean" - log_provenance( - f"{local_path}/{wt_string}_{wt}{driver}_" - f"{data_info.get('dataset')}_{data_info.get('ensemble', '')}" - f"_{var_name}_mean_{data_info.get('timerange')}", - cfg, - provenance_record, - ) + log_provenance( + f"{local_path}/{wt_string}_{wt}{driver}_" + f"{data_info.get('dataset')}_{data_info.get('ensemble', '')}" + f"_{var_name}_mean_{data_info.get('timerange')}", + cfg, + provenance_record, + ) def get_wt_array(wt_string: str, wt_cubes: iris.cube.CubeList) -> tuple: @@ -900,25 +900,25 @@ def calc_wt_anomalies( "anomaly", ) - ancestors = [ - f"{data_info.get('preproc_path')}", - f"{cfg.get('work_dir')}/ERA5.nc", - ] - provenance_record = get_provenance_record( - f"{var_name} anomaly for \ - {wt_string}", - ancestors, - [var_name], - ["map"], - ["anomaly"], - ) + ancestors = [ + f"{data_info.get('preproc_path')}", + f"{cfg.get('work_dir')}/ERA5.nc", + ] + provenance_record = get_provenance_record( + f"{var_name} anomaly for, wt: {wt} \ + {wt_string}", + ancestors, + [var_name], + ["map"], + ["anomaly"], + ) - log_provenance( - f"{cfg.get('plot_dir')}/anomaly/{wt_string}_{wt}_{data_info.get('dataset')}_{data_info.get('ensemble', '')}" - f"_{var_name}_anomaly__{data_info.get('timerange')}", - cfg, - provenance_record, - ) + log_provenance( + f"{cfg.get('plot_dir')}/anomaly/{wt_string}_{wt}_{data_info.get('dataset')}_{data_info.get('ensemble', '')}" + f"_{var_name}_anomaly__{data_info.get('timerange')}", + cfg, + provenance_record, + ) def calc_wt_std( @@ -971,28 +971,28 @@ def calc_wt_std( wt_cube_std = extracted_cube.collapsed("time", iris.analysis.STD_DEV) plot_maps(wt, cfg, wt_cube_std, data_info, "stddev") - ancestors = [ - f"{data_info.get('preproc_path')}", - f"{cfg.get('work_dir')}/ERA5.nc", - ] - provenance_record = get_provenance_record( - f"{var_name} standard \ - deviation for \ - {wt_string}", - ancestors, - [var_name], - ["map"], - ["stddev"], - ) + ancestors = [ + f"{data_info.get('preproc_path')}", + f"{cfg.get('work_dir')}/ERA5.nc", + ] + provenance_record = get_provenance_record( + f"{var_name} standard, wt: {wt}\ + deviation for \ + {wt_string}", + ancestors, + [var_name], + ["map"], + ["stddev"], + ) - local_path = f"{cfg.get('plot_dir')}/stddev" + local_path = f"{cfg.get('plot_dir')}/stddev" - log_provenance( - f"{local_path}/{wt_string}_{wt}_{data_info.get('dataset')}_{data_info.get('ensemble', '')}" - f"_{var_name}_stddev_{data_info.get('timerange')}", - cfg, - provenance_record, - ) + log_provenance( + f"{local_path}/{wt_string}_{wt}_{data_info.get('dataset')}_{data_info.get('ensemble', '')}" + f"_{var_name}_stddev_{data_info.get('timerange')}", + cfg, + provenance_record, + ) def calc_lwt_model(cfg: dict, cube: iris.cube.Cube, data_info: dict): diff --git a/esmvaltool/diag_scripts/weathertyping/plot_utils.py b/esmvaltool/diag_scripts/weathertyping/plot_utils.py index 2334596877..6c404bcdfa 100644 --- a/esmvaltool/diag_scripts/weathertyping/plot_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/plot_utils.py @@ -152,6 +152,27 @@ def plot_seasonal_occurrence( f"{wt_string}_rel_occurrence_{data_info.get('timerange')}.pdf", ) plt.close() + # ancestors here are just the wt_cubes i guess + ancestors = [ + f"{data_info.get('preproc_path')}", + f"{cfg.get('work_dir')}/ERA5.nc", + ] + provenance_record = get_provenance_record( + f"Seasonal occurrences for {wt_string}, ", + ancestors, + ["wt occurrences"], + ["stackplot"], + ) + + local_path = f"{cfg.get('plot_dir')}/mean" + + log_provenance( + f"{local_path}/{wt_string}_{driver}_" + f"{data_info.get('dataset')}_{data_info.get('ensemble', '')}" + f"_seasonal_occurrence_{data_info.get('timerange')}", + cfg, + provenance_record, + ) def set_gridlines(ax: plt.Axes): From 5c52fa1a10770b6eeac0bd7abb99661ca2c0a817 Mon Sep 17 00:00:00 2001 From: Thomas Kroissenbrunner Date: Thu, 8 Jan 2026 10:47:01 +0100 Subject: [PATCH 53/57] update ancestors for seasonal occurrence provenance --- .../diag_scripts/weathertyping/calc_utils.py | 21 +++++---- .../diag_scripts/weathertyping/plot_utils.py | 44 ++++++++++++++++--- .../weathertyping/weathertyping.py | 43 ++++++++++-------- 3 files changed, 76 insertions(+), 32 deletions(-) diff --git a/esmvaltool/diag_scripts/weathertyping/calc_utils.py b/esmvaltool/diag_scripts/weathertyping/calc_utils.py index 5870c098bb..86e8ba2584 100644 --- a/esmvaltool/diag_scripts/weathertyping/calc_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/calc_utils.py @@ -81,8 +81,8 @@ def calc_slwt_obs( wt_data_prcp = [] for wt_ in range(1, 28): - target_indices = da.where(lwt == wt_).compute() - if not target_indices[0]: + target_indices = da.where(lwt == wt_)[0].compute() + if target_indices.size == 0: logger.info( "calc_slwt_obs - CAUTION: Skipped wt %s \ for dataset %s!", @@ -98,7 +98,7 @@ def calc_slwt_obs( extracted_cube = cube[target_indices] else: extracted_cube = cube.extract( - iris.Constraint(time=lambda t, d=dates: t.point in d[0]), + iris.Constraint(time=lambda t, d=dates: t.point in d), ) wt_cube_mean = extracted_cube.collapsed("time", iris.analysis.MEAN) @@ -597,16 +597,19 @@ def calc_lwt_slwt_model( f"{cfg.get('work_dir')}/wt_mapping_dict_ERA5.json", ).open("r", encoding="utf-8") as file: mapping_dict_era5_f = json.load(file) + mapping_dict_era5 = reverse_convert_dict(mapping_dict_era5_f) with Path( f"{cfg.get('work_dir')}/wt_mapping_dict_E-OBS.json", ).open("r", encoding="utf-8") as file: mapping_dict_eobs_f = json.load(file) - mapping_dict_era5 = reverse_convert_dict(mapping_dict_era5_f) - mapping_dict_eobs = reverse_convert_dict(mapping_dict_eobs_f) + mapping_dict_eobs = reverse_convert_dict(mapping_dict_eobs_f) slwt_era5 = map_lwt_to_slwt(lwt, mapping_dict_era5) slwt_eobs = map_lwt_to_slwt(lwt, mapping_dict_eobs) + + print("ERA5: ", slwt_era5) + print("EOBS: ", slwt_eobs) else: predefined_slwt = check_mapping_dict_format(predefined_slwt) write_mapping_dict(cfg.get("work_dir"), "ERA5", predefined_slwt) @@ -705,7 +708,7 @@ def process_prcp_mean( "correlation_threshold", ) and rmse_matrix[i][j] <= cfg.get("rmse_threshold"): selected_pairs.append( - ((i + 1, j + 1),), + (i + 1, j + 1), ) # write matrices to csv @@ -762,7 +765,7 @@ def calc_wt_means( for wt in range(1, len(counts)): target_indices = da.where(wt_array == wt)[0].compute() - if len(target_indices) < 1: + if target_indices.size == 0: logger.info( "calc_wt_means - CAUTION: Skipped %s %s \ for dataset %s!", @@ -877,7 +880,7 @@ def calc_wt_anomalies( for wt in range(1, len(np.unique(wt_array)) + 1): target_indices = da.where(wt_array == wt).compute() - if not target_indices[0]: + if target_indices.size == 0: logger.info( "calc_wt_anomalies - CAUTION: Skipped wt %s \ for dataset %s!", @@ -954,7 +957,7 @@ def calc_wt_std( for wt in range(1, len(np.unique(wt_array)) + 1): target_indices = da.where(wt_array == wt).compute() - if not target_indices[0]: + if target_indices.size == 0: logger.info( "calc_slwt_obs - CAUTION: Skipped wt %s \ for dataset %s!", diff --git a/esmvaltool/diag_scripts/weathertyping/plot_utils.py b/esmvaltool/diag_scripts/weathertyping/plot_utils.py index 6c404bcdfa..6f669dfb3f 100644 --- a/esmvaltool/diag_scripts/weathertyping/plot_utils.py +++ b/esmvaltool/diag_scripts/weathertyping/plot_utils.py @@ -50,6 +50,7 @@ def plot_seasonal_occurrence( cfg: dict, wt_cubes: iris.cube.Cube, data_info: dict, + cube_path: str, ): """Plot seasonal occurrences of weathertypes. @@ -61,6 +62,8 @@ def plot_seasonal_occurrence( List of cubes of lwt, slwt_ERA5 and slwt_EOBS data_info Dictionary with info to dataset + cube_path + Paths to weathertype cubes (ancestors) """ driver = get_driver(data_info) @@ -154,14 +157,13 @@ def plot_seasonal_occurrence( plt.close() # ancestors here are just the wt_cubes i guess ancestors = [ - f"{data_info.get('preproc_path')}", - f"{cfg.get('work_dir')}/ERA5.nc", + f"{cube_path}", ] provenance_record = get_provenance_record( f"Seasonal occurrences for {wt_string}, ", ancestors, ["wt occurrences"], - ["stackplot"], + ["seas"], ) local_path = f"{cfg.get('plot_dir')}/mean" @@ -381,8 +383,8 @@ def plot_corr_rmse_heatmaps( Parameters ---------- - cf - gConfiguration dictionary from recipe + cfg + Configuration dictionary from recipe pattern_correlation_matrix Pattern correlation matrix rmse_matrix @@ -458,6 +460,38 @@ def plot_corr_rmse_heatmaps( plt.savefig(f"{output_path}/rmse_matrix_{dataset}_{timerange}.png") plt.savefig(f"{output_path}/rmse_matrix_{dataset}_{timerange}.pdf") plt.close() + # log provenance + # rmse matrix + ancestors = [ + f"{cfg.get('work_dir')}/ERA5.nc", + ] + provenance_record = get_provenance_record( + "rmse matrix", + ancestors, + ["rmse"], + ["other"], + ) + + local_path = f"{cfg.get('plot_dir')}/" + + log_provenance( + f"{local_path}/rmse_matrix_{dataset}_{timerange}.png", + cfg, + provenance_record, + ) + # correlation matrix + provenance_record = get_provenance_record( + "correlation matrix", + ancestors, + ["correlation"], + ["other"], + ) + + log_provenance( + f"{local_path}/correlation_matrix_{dataset}_{timerange}.png", + cfg, + provenance_record, + ) def get_colormap(colormap_string: str) -> ListedColormap: diff --git a/esmvaltool/diag_scripts/weathertyping/weathertyping.py b/esmvaltool/diag_scripts/weathertyping/weathertyping.py index 50fd47d42d..df06718695 100644 --- a/esmvaltool/diag_scripts/weathertyping/weathertyping.py +++ b/esmvaltool/diag_scripts/weathertyping/weathertyping.py @@ -78,12 +78,13 @@ def process_models_automatic_slwt( # plot means if cfg.get("plotting", False): # load wt files + wt_cube_path = f"{cfg.get('work_dir')}/{output_file_path}" + f"/{data_info['dataset']}" + f"{data_info['driver']}_" + f"{data_info['timerange']}.nc" + wt_cubes = load_wt_files( - f"{cfg.get('work_dir')}/{output_file_path}" - f"/{data_info['dataset']}" - f"{data_info['driver']}_" - f"{data_info['ensemble']}_" - f"{data_info['timerange']}.nc", + wt_cube_path, f"{data_info['ensemble']}_" ) var_dict = { @@ -96,7 +97,9 @@ def process_models_automatic_slwt( data_info["preproc_path"] = var_data[1] plot_means(cfg, var_data[0], wt_cubes, data_info) - plot_seasonal_occurrence(cfg, wt_cubes, data_info) + plot_seasonal_occurrence( + cfg, wt_cubes, data_info, wt_cube_path + ) def process_era5_automatic_slwt( @@ -163,9 +166,8 @@ def process_era5_automatic_slwt( combine_wt_to_file(cfg, wt_list, wt_preproc, data_info["dataset"]) # load weathertype files as cubes - wt_cubes = load_wt_files( - f"{cfg.get('work_dir')}/{data_info['dataset']}.nc", - ) + wt_cube_path = f"{cfg.get('work_dir')}/{data_info['dataset']}.nc" + wt_cubes = load_wt_files(wt_cube_path) if cfg.get("plotting", False): var_dict = get_looping_dict( @@ -177,7 +179,7 @@ def process_era5_automatic_slwt( data_info["preproc_path"] = var_data[1] plot_means(cfg, var_data[0], wt_cubes, data_info) - plot_seasonal_occurrence(cfg, wt_cubes, data_info) + plot_seasonal_occurrence(cfg, wt_cubes, data_info, wt_cube_path) def run_automatic_slwt(cfg: dict): @@ -256,8 +258,9 @@ def process_era5_lwt( write_lwt_to_file(cfg, lwt, wt_preproc, data_info["dataset"]) # load wt files + wt_cube_path = f"{cfg.get('work_dir')}/{data_info['dataset']}.nc" wt_cubes = load_wt_files( - f"{cfg.get('work_dir')}/{data_info['dataset']}.nc", + wt_cube_path, mode="lwt", ) @@ -271,7 +274,7 @@ def process_era5_lwt( data_info["preproc_path"] = var_data[1] plot_means(cfg, var_data[0], wt_cubes, data_info, "lwt") - plot_seasonal_occurrence(cfg, wt_cubes, data_info) + plot_seasonal_occurrence(cfg, wt_cubes, data_info, wt_cube_path) def process_models_lwt(cfg: dict, dataset_vars: list, data_info: dict): @@ -308,12 +311,14 @@ def process_models_lwt(cfg: dict, dataset_vars: list, data_info: dict): calc_lwt_model(cfg, wt_preproc, data_info) # load wt files + wt_cube_path = f"{cfg.get('work_dir')}/{output_file_path}" + f"/{data_info['dataset']}" + f"{data_info['driver']}" + f"_{data_info['ensemble']}_" + f"{data_info['timerange']}.nc" + wt_cubes = load_wt_files( - f"{cfg.get('work_dir')}/{output_file_path}" - f"/{data_info['dataset']}" - f"{data_info['driver']}" - f"_{data_info['ensemble']}_" - f"{data_info['timerange']}.nc", + wt_cube_path, mode="lwt", ) @@ -334,7 +339,9 @@ def process_models_lwt(cfg: dict, dataset_vars: list, data_info: dict): data_info, "lwt", ) - plot_seasonal_occurrence(cfg, wt_cubes, data_info) + plot_seasonal_occurrence( + cfg, wt_cubes, data_info, wt_cube_path + ) def run_lwt(cfg: dict): From 2021b99338794f98eed8396b3db91cfa28da3c6f Mon Sep 17 00:00:00 2001 From: Thomas Kroissenbrunner Date: Thu, 8 Jan 2026 11:14:13 +0100 Subject: [PATCH 54/57] . --- esmvaltool/diag_scripts/weathertyping/weathertyping.py | 1 - 1 file changed, 1 deletion(-) diff --git a/esmvaltool/diag_scripts/weathertyping/weathertyping.py b/esmvaltool/diag_scripts/weathertyping/weathertyping.py index df06718695..2c0b6aa2d0 100644 --- a/esmvaltool/diag_scripts/weathertyping/weathertyping.py +++ b/esmvaltool/diag_scripts/weathertyping/weathertyping.py @@ -74,7 +74,6 @@ def process_models_automatic_slwt( data_info, cfg.get("predefined_slwt"), ) - # plot means if cfg.get("plotting", False): # load wt files From 02136098a4c266e1c8115fccb57f469a40888d8b Mon Sep 17 00:00:00 2001 From: Thomas Kroissenbrunner Date: Thu, 8 Jan 2026 11:20:33 +0100 Subject: [PATCH 55/57] minor issues --- esmvaltool/diag_scripts/weathertyping/weathertyping.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/esmvaltool/diag_scripts/weathertyping/weathertyping.py b/esmvaltool/diag_scripts/weathertyping/weathertyping.py index 2c0b6aa2d0..667cf9bdc2 100644 --- a/esmvaltool/diag_scripts/weathertyping/weathertyping.py +++ b/esmvaltool/diag_scripts/weathertyping/weathertyping.py @@ -83,7 +83,8 @@ def process_models_automatic_slwt( f"{data_info['timerange']}.nc" wt_cubes = load_wt_files( - wt_cube_path, f"{data_info['ensemble']}_" + wt_cube_path, + f"{data_info['ensemble']}_", ) var_dict = { @@ -97,7 +98,10 @@ def process_models_automatic_slwt( plot_means(cfg, var_data[0], wt_cubes, data_info) plot_seasonal_occurrence( - cfg, wt_cubes, data_info, wt_cube_path + cfg, + wt_cubes, + data_info, + wt_cube_path, ) From 1ec731f0ab7fe4ec3afd331d485c5867a5e7ce42 Mon Sep 17 00:00:00 2001 From: Thomas Kroissenbrunner Date: Thu, 8 Jan 2026 11:29:22 +0100 Subject: [PATCH 56/57] minor fix! --- esmvaltool/diag_scripts/weathertyping/weathertyping.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/esmvaltool/diag_scripts/weathertyping/weathertyping.py b/esmvaltool/diag_scripts/weathertyping/weathertyping.py index 667cf9bdc2..8ef79ec613 100644 --- a/esmvaltool/diag_scripts/weathertyping/weathertyping.py +++ b/esmvaltool/diag_scripts/weathertyping/weathertyping.py @@ -343,7 +343,10 @@ def process_models_lwt(cfg: dict, dataset_vars: list, data_info: dict): "lwt", ) plot_seasonal_occurrence( - cfg, wt_cubes, data_info, wt_cube_path + cfg, + wt_cubes, + data_info, + wt_cube_path, ) From f830b237be0175125e65b34fc4630ab5fbaea70c Mon Sep 17 00:00:00 2001 From: Thomas Kroissenbrunner Date: Thu, 8 Jan 2026 13:01:46 +0100 Subject: [PATCH 57/57] bug --- .../weathertyping/weathertyping.py | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/esmvaltool/diag_scripts/weathertyping/weathertyping.py b/esmvaltool/diag_scripts/weathertyping/weathertyping.py index 8ef79ec613..0f8da4d277 100644 --- a/esmvaltool/diag_scripts/weathertyping/weathertyping.py +++ b/esmvaltool/diag_scripts/weathertyping/weathertyping.py @@ -77,16 +77,15 @@ def process_models_automatic_slwt( # plot means if cfg.get("plotting", False): # load wt files - wt_cube_path = f"{cfg.get('work_dir')}/{output_file_path}" - f"/{data_info['dataset']}" - f"{data_info['driver']}_" - f"{data_info['timerange']}.nc" - - wt_cubes = load_wt_files( - wt_cube_path, - f"{data_info['ensemble']}_", + wt_cube_path = ( + f"{cfg.get('work_dir')}/{output_file_path}" + f"/{data_info['dataset']}" + f"{data_info['driver']}_" + f"{data_info['timerange']}.nc" ) + wt_cubes = load_wt_files(wt_cube_path) + var_dict = { f"{ensemble_var.get('short_name')}": get_preproc_lists_ensemble( ensemble_var, @@ -314,11 +313,13 @@ def process_models_lwt(cfg: dict, dataset_vars: list, data_info: dict): calc_lwt_model(cfg, wt_preproc, data_info) # load wt files - wt_cube_path = f"{cfg.get('work_dir')}/{output_file_path}" - f"/{data_info['dataset']}" - f"{data_info['driver']}" - f"_{data_info['ensemble']}_" - f"{data_info['timerange']}.nc" + wt_cube_path = ( + f"{cfg.get('work_dir')}/{output_file_path}" + f"/{data_info['dataset']}" + f"{data_info['driver']}" + f"_{data_info['ensemble']}_" + f"{data_info['timerange']}.nc" + ) wt_cubes = load_wt_files( wt_cube_path,