From 5bfd59f273cbd671bbfea589c79d5cb4e2eb536c Mon Sep 17 00:00:00 2001 From: Jens-Uwe Grooss Date: Fri, 21 Mar 2025 18:38:55 +0000 Subject: [PATCH 1/5] decouple download freom convert of ECMWF data --- bin/convert.sh | 13 ++- bin/convert_all.sh | 116 +++++++++++++++++++++++ bin/get_ecmwf_aviso.sh | 206 +++++++++++++++++++++++++++++++++++++++++ settings.default | 2 +- 4 files changed, 335 insertions(+), 2 deletions(-) create mode 100755 bin/convert_all.sh create mode 100755 bin/get_ecmwf_aviso.sh diff --git a/bin/convert.sh b/bin/convert.sh index 390f617..716ddb9 100755 --- a/bin/convert.sh +++ b/bin/convert.sh @@ -69,9 +69,20 @@ if [[ x$SFC_PARAMETERS != x"" ]]; then -a standard_name,SSTK,o,c,sea_surface_temperature \ -a standard_name,U10M,o,c,surface_eastward_wind \ -a standard_name,V10M,o,c,surface_northward_wind \ - -a units,HCC,o,c,dimensionless \ + -a standard_name,TCW,o,c,total_column_water \ + -a standard_name,TCWV,o,c,total_column_cloud_liquid_water \ + -a standard_name,T2M,o,c,2_meter_temperature \ + -a standard_name,D2M,o,c,2_meter_dewpoint \ + -a standard_name,ISHF,o,c,instantaneous_surface_sensible_heat_flux\ + -a standard_name,IEWS,o,c,instantaneous_x_surface_stress\ + -a standard_name,INSS,o,c,instantaneous_y_surface_stress\ + -a units,HCC,o,c,dimensionless \ -a units,LCC,o,c,dimensionless \ -a units,MCC,o,c,dimensionless \ + -a units,CI,o,c,dimensionless \ + -a units,LSM,o,c,dimensionless \ + -a units,ASN,o,c,dimensionless \ + -a units,SD,o,c,m \ $sfcfile fi # extract lnsp and remove lev dimension. diff --git a/bin/convert_all.sh b/bin/convert_all.sh new file mode 100755 index 0000000..dd1343b --- /dev/null +++ b/bin/convert_all.sh @@ -0,0 +1,116 @@ +#!/bin/bash +#Copyright (C) 2025 by Forschungszentrum Juelich GmbH +#Author(s): Jens-Uw Grooss + +export MAINDIR=$HOME/data-retrieval +export BINDIR=$MAINDIR/bin + +. ${MAINDIR}/settings.default + +if [ ! -f ${MAINDIR}/settings.config ]; then + echo Please copy the settings.example to settings.config and configure your setup! + exit 1 +fi + +. ${MAINDIR}/settings.config + +cd $WORKDIR + +export YEAR=`date +%Y` +export MONTH=`date +%m` +export DAY=`date +%d` +AMPM=`date +%p` +if [ $AMPM == AM ] +then + export HH=00 +else + export HH=12 +fi + + +# script should start at 06h/18h to look for 00 12h forecast +export h_exit=`date --date="+6hours" +%H` + +for FCSTEP in 036 072 108 144 228 +do + +# Set path, filenames and variables used later in the script + export DATE=${YEAR}-${MONTH}-${DAY} + export YMD=${YEAR}${MONTH}${DAY} + export TIME=${HH}:00:00 + export BASE=${DATASET}.${YMD}T${HH}.${FCSTEP} + export init_date=${DATE}T${TIME} + echo BASE: $BASE + + lockfile=grib/${DATASET}.${YMD}T${HH}.${FCSTEP}.ready + echo `date` waiting for lockfile $lockfile + until test -e $lockfile + do + sleep 30 + h_now=`date +%H` + if [[ $h_now -ne $h_exit ]] + then + echo lockfile $lockfile not found by `date` + echo exiting script + exit 1 + fi + done + rm $lockfile + # Convert grib to netCDF, set init time + echo `date`: converting ${FCSTEP}h forecast + . $BINDIR/convert.sh + + if [ $ECTRANS_ID == "none" ] + then + echo "no ectrans transfer -- move data to " $MSSDIR + if [ x$TRANSFER_MODEL_LEVELS == x"yes" ]; then + mv $mlfile $MSSDIR + fi + if [ -f $tlfile ]; then + mv $tlfile $MSSDIR + fi + if [ -f $plfile ]; then + mv $plfile $MSSDIR + fi + if [ -f $pvfile ]; then + mv $pvfile $MSSDIR + + fi + if [ -f $alfile ]; then + mv $alfile $MSSDIR + fi + if [ -f $sfcfile ]; then + mv $sfcfile $MSSDIR + fi + fi +done +echo `date`: converting finished + + +if [[ x$CLEANUP == x"yes" ]] +then + export CYMD=${CLEANUP_YEAR}${CLEANUP_MONTH}${CLEANUP_DAY} + export CBASE=${DATASET}.${CYMD}T${HH}.${FCSTEP} + echo cleanup $CBASE + + # clean up locally + for f in $mlfile $tlfile $plfile $pvfile $alfile $sfcfile grib/${CBASE}*.grib; + do + if [ -f $f ]; + then + rm $f + fi + done + if [ $ECTRANS_ID == "none" ] + then + # clean up MSS server dir + for f in $MSSDIR/${CBASE}*.nc + do + if [ -f $f ]; + then + rm $f + fi + done + fi +fi +echo `date`: ECMWF data converson finished diff --git a/bin/get_ecmwf_aviso.sh b/bin/get_ecmwf_aviso.sh new file mode 100755 index 0000000..e8f7b72 --- /dev/null +++ b/bin/get_ecmwf_aviso.sh @@ -0,0 +1,206 @@ +#!/bin/bash + +#Copyright (C) 2021 by Forschungszentrum Juelich GmbH +#Author(s): Joern Ungermann, May Baer, Jens-Uwe Grooss + +#SBATCH --qos=normal +#SBATCH --job-name=get_ecmwf +#SBATCH --output=get_ecmwf.%j.out +#SBATCH --error=get_ecmwf.%j.out + + +# This script works with the cdo version installed on ECACCESS and +# in an mambaforge environment ncenv that includes cartopy (0.20.1), metpy (1.1.0) +# nco (5.0.4), netcdf4 (1.5.8), scipy (1.7.3) and xarray (0.20.2) + +# Define model domain sector, resolution and id name for ectrans in settings.config + +# defines for performance measurements +N=`date +%s%N` +export PS4='+[$(((`date +%s%N`-$N)/1000000))ms][${BASH_SOURCE}:${LINENO}]: ${FUNCNAME[0]:+${FUNCNAME[0]}(): }' +# enable line below for debugging and performance timing +# set -x +export MAINDIR=$HOME/data-retrieval +export BINDIR=$MAINDIR/bin + +. ${MAINDIR}/settings.default + +if [ ! -f ${MAINDIR}/settings.config ]; then + echo Please copy the settings.example to settings.config and configure your setup! + exit 1 +fi + +. ${MAINDIR}/settings.config + +# get forecast date +# If used as a shell script that is run on a event trigger, +# the $MSJ* environment variables contain the corresponding time info. +# This can be done from the web interface or e.g. by the command +# ecaccess-job-submit -ni fc00h036 get_ecmwf.sh +# If these variables are empty, forecast times are defined in settings.config + + +# aviso trigger + +POSITIONAL=() +while [[ $# -gt 0 ]] +do +key="$1" + +case $key in + --date) + AVISO_DATE="$2" + shift # past argument + shift # past value + ;; + --stream) + STREAM="$2" + shift # past argument + shift # past value + ;; + --time) + AVISO_HH="$2" + shift # past argument + shift # past value + ;; + --step) + AVISO_STEP="$2" + shift # past argument + shift # past value + ;; +esac +done + +echo `date`: Notification received for stream $STREAM, date $AVISO_DATE, time $AVISO_HH, step $AVISO_STEP +##20250304 00 72 +export YEAR=${AVISO_DATE:0:4} +export MONTH=${AVISO_DATE:4:2} +export DAY=${AVISO_DATE:6:2} +export HH=$AVISO_HH +export FCSTEP=`printf '%03d' $AVISO_STEP` + +case $FCSTEP in + 036) + export STEP=0/to/36/by/3 + ;; + 072) + export STEP=39/to/72/by/3 + ;; + 108) + export STEP=75/to/108/by/3 + ;; + 144) + export STEP=114/to/144/by/6 + ;; + 228) + export STEP=156/to/228/by/12 + ;; + *) +esac + +cd $WORKDIR +mkdir -p mss +mkdir -p grib + +# Set path, filenames and variables used later in the script +export DATE=${YEAR}-${MONTH}-${DAY} +export YMD=${YEAR}${MONTH}${DAY} +export TIME=${HH}:00:00 +export BASE=${DATASET}.${YMD}T${HH}.${FCSTEP} +export init_date=$(date +%Y-%m-%dT%H:%M:%S) +echo BASE: $BASE + +if [[ "$init_date" > "$DATE" ]] +then + export init_date="${DATE}T${TIME}" +fi +export time_units="hours since ${init_date}" + +# Retrieve ml, sfc, pv and pt files +. $BINDIR/download_ecmwf.sh + +if [ $DOWNLOAD_ONLY == "yes" ] +then + lockfile=grib/${BASE}.ready + echo touch $lockfile and exit + touch $lockfile + exit 1 +fi + +# Convert grib to netCDF, set init time +. $BINDIR/convert.sh + +if [ $ECTRANS_ID == "none" ] +then + echo "no ectrans transfer -- move data to " $MSSDIR + if [ x$TRANSFER_MODEL_LEVELS == x"yes" ]; then + mv $mlfile $MSSDIR + fi + if [ -f $tlfile ]; then + mv $tlfile $MSSDIR + fi + if [ -f $plfile ]; then + mv $plfile $MSSDIR + fi + if [ -f $pvfile ]; then + mv $pvfile $MSSDIR + + fi + if [ -f $alfile ]; then + mv $alfile $MSSDIR + fi + if [ -f $sfcfile ]; then + mv $sfcfile $MSSDIR + fi +else + +if ecaccess-association-list | grep -q $ECTRANS_ID; then + echo "Transfering files to "$ECTRANS_ID + if [ x$TRANSFER_MODEL_LEVELS == x"yes" ]; then + ectrans -remote $ECTRANS_ID -source $mlfile -target $mlfile -overwrite -remove + fi + if [ -f $tlfile ]; then + ectrans -remote $ECTRANS_ID -source $tlfile -target $tlfile -overwrite -remove + fi + if [ -f $plfile ]; then + ectrans -remote $ECTRANS_ID -source $plfile -target $plfile -overwrite -remove + fi + if [ -f $pvfile ]; then + ectrans -remote $ECTRANS_ID -source $pvfile -target $pvfile -overwrite -remove + fi + if [ -f $alfile ]; then + ectrans -remote $ECTRANS_ID -source $alfile -target $alfile -overwrite -remove + fi + if [ -f $sfcfile ]; then + ectrans -remote $ECTRANS_ID -source $sfcfile -target $sfcfile -overwrite -remove + fi +fi +fi + +if [[ x$CLEANUP == x"yes" ]] +then + export CYMD=${CLEANUP_YEAR}${CLEANUP_MONTH}${CLEANUP_DAY} + export CBASE=${DATASET}.${CYMD}T${HH}.${FCSTEP} + echo cleanup $CBASE + + # clean up locally + for f in $mlfile $tlfile $plfile $pvfile $alfile $sfcfile grib/${CBASE}*.grib; + do + if [ -f $f ]; + then + rm $f + fi + done + if [ $ECTRANS_ID == "none" ] + then + # clean up MSS server dir + for f in $MSSDIR/${CBASE}*.nc + do + if [ -f $f ]; + then + rm $f + fi + done + fi +fi +echo `date`: ECMWF data converson finished diff --git a/settings.default b/settings.default index 591c261..2d15f34 100644 --- a/settings.default +++ b/settings.default @@ -17,7 +17,7 @@ export ANCILLARY="--pv --theta --tropopause --n2 --pressure" export MODEL_LEVELS=1/to/137 export MODEL_PARAMETERS=T/U/V/W/Q/CC/CLWC/CIWC/D/O3 export MODEL2_PARAMETERS=LNSP/Z -export SFC_PARAMETERS=LSM/LCC/MCC/HCC/MSL/SP/SSTK/TCC/TCW/TCWV/10U/10V/2D/2T/SKT/ASN/CI/BLH/CAPE/SD/IEWS/INSS/ISHF +export SFC_PARAMETERS=LSM/LCC/MCC/HCC/MSL/SP/SSTK/TCC/TCW/TCWV/10U/10V/2D/2T/SKT/ASN/CI/BLH/SD/IEWS/INSS/ISHF export PV_PARAMETERS=Z/PRES/PT/U/V/Q/O3 export PV_LEVELS=2000 From 132260fad441e2db353c2292c59451778cd314e2 Mon Sep 17 00:00:00 2001 From: Jens-Uwe Grooss Date: Fri, 21 Mar 2025 18:45:38 +0000 Subject: [PATCH 2/5] decouple download freom convert of ECMWF data -- adaption of the configuration file --- settings.default | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/settings.default b/settings.default index 2d15f34..1e03e73 100644 --- a/settings.default +++ b/settings.default @@ -6,6 +6,13 @@ export CLEANUP=no export MODEL_REDUCTION="-d lev,0,0 -d lev,16,28,4 -d lev,32,124,2" export DATASET=ecmwf +# set ECTRANS_ID to "none" if write to local $MSSDIR +export ECTRANS_ID=none + +# set DOWNLOAD_ONLY to yes if conversion is done by separate script +export DOWNLOAD_ONLY=no + + export TRUNCATION=auto # options: none, auto, TXXX (e.g. T21) export RESOL=auto # options: av (archived), auto, TXXX(e.g. T21); truncation shall replace resol but resol is still needed export ECMWF_TYPE=fc From ab08103f3cd025bdc259c1ef03da4ad5c14b1ce2 Mon Sep 17 00:00:00 2001 From: Jens-Uwe Grooss Date: Sun, 23 Mar 2025 10:58:20 +0000 Subject: [PATCH 3/5] typo in lockfile exit added missing time units --- bin/convert_all.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bin/convert_all.sh b/bin/convert_all.sh index dd1343b..5d896c1 100755 --- a/bin/convert_all.sh +++ b/bin/convert_all.sh @@ -41,6 +41,8 @@ do export BASE=${DATASET}.${YMD}T${HH}.${FCSTEP} export init_date=${DATE}T${TIME} echo BASE: $BASE + export time_units="hours since ${init_date}" + echo time_units: "hours since ${init_date}" lockfile=grib/${DATASET}.${YMD}T${HH}.${FCSTEP}.ready echo `date` waiting for lockfile $lockfile @@ -48,7 +50,7 @@ do do sleep 30 h_now=`date +%H` - if [[ $h_now -ne $h_exit ]] + if [[ $h_now == $h_exit ]] then echo lockfile $lockfile not found by `date` echo exiting script From 50b3340d213169ad5f31c76acf6983d8994274ff Mon Sep 17 00:00:00 2001 From: Jens-Uwe Grooss Date: Mon, 24 Mar 2025 13:50:48 +0000 Subject: [PATCH 4/5] Inclide trigger starting conversion jobs using the aviso system --- aviso/aviso_readme.txt | 29 +++++++++++++++++++ aviso/config.yaml.parallel | 56 ++++++++++++++++++++++++++++++++++++ aviso/config.yaml.sequential | 12 ++++++++ bin/convert_all.sh | 6 ++-- settings.default | 4 +++ 5 files changed, 104 insertions(+), 3 deletions(-) create mode 100644 aviso/aviso_readme.txt create mode 100644 aviso/config.yaml.parallel create mode 100644 aviso/config.yaml.sequential diff --git a/aviso/aviso_readme.txt b/aviso/aviso_readme.txt new file mode 100644 index 0000000..6f081c9 --- /dev/null +++ b/aviso/aviso_readme.txt @@ -0,0 +1,29 @@ +Use of aviso to trigger job scripts on the European Weather Cloud (EWC) +======================================================================= + + +* The aviso service runs automatically on EWC machines with the data setup + It can be activated and deactivated by the command + sudo systemctl start aviso.service + sudo systemctl stop aviso.service + The status can be viewd be + sudo systemctl status aviso.service + +* For test purposes it my be used to test the behaviour on past trigger + events for a given (past) time range. This can be done by the listen + command within an env, where aviso is installed, like + sudo systemctl stop aviso.service + mamba activate avisoenv + aviso listen --from 2025-03-05T00:00:00.0Z --to 2025-03-05T08:00:00.0Z + +* The file config.yaml in the direactory $HOME/.aviso guides the triggering + +* if multiple steps are given in a trigger command, like + step: [36,72,144] + the programs called by the trigger are executed sequentially, possibly + not in the time order of the triggers + +* the script get_ecmwf_aviso.sh creates ECMWF grib files and converts + them to MSS-convorm NetCDF files. One can speed up the process by + parallel download the grib files using "DOWNLOAD_ONLY=yes" in the file + settings.config and starting convert_all.sh separately. diff --git a/aviso/config.yaml.parallel b/aviso/config.yaml.parallel new file mode 100644 index 0000000..9df4315 --- /dev/null +++ b/aviso/config.yaml.parallel @@ -0,0 +1,56 @@ +listeners: + - event: mars + request: + class: od + expver: 1 + domain: g + stream: oper + step: 36 + triggers: + - type: command + working_dir: $HOME/data-retrieval/aviso + command: $HOME/data-retrieval/bin/get_ecmwf_aviso.sh --date ${request.date} --time ${request.time} --step ${request.step} > mars_${request.time}_${request.step}.out + - event: mars + request: + class: od + expver: 1 + domain: g + stream: oper + step: 72 + triggers: + - type: command + working_dir: $HOME/aviso + command: $HOME/data-retrieval/bin/get_ecmwf_aviso.sh --date ${request.date} --time ${request.time} --step ${request.step} > mars_${request.time}_${request.step}.out + - event: mars + request: + class: od + expver: 1 + domain: g + stream: oper + step: 108 + triggers: + - type: command + working_dir: $HOME/aviso + command: $HOME/data-retrieval/bin/get_ecmwf_aviso.sh --date ${request.date} --time ${request.time} --step ${request.step} > mars_${request.time}_${request.step}.out + - event: mars + request: + class: od + expver: 1 + domain: g + stream: oper + step: 144 + triggers: + - type: command + working_dir: $HOME/aviso + command: $HOME/data-retrieval/bin/get_ecmwf_aviso.sh --date ${request.date} --time ${request.time} --step ${request.step} > mars_${request.time}_${request.step}.out + - event: mars + request: + class: od + expver: 1 + domain: g + stream: oper + step: 228 + triggers: + - type: command + working_dir: $HOME/aviso + command: $HOME/data-retrieval/bin/get_ecmwf_aviso.sh --date ${request.date} --time ${request.time} --step ${request.step} > mars_${request.time}_${request.step}.out diff --git a/aviso/config.yaml.sequential b/aviso/config.yaml.sequential new file mode 100644 index 0000000..95a9821 --- /dev/null +++ b/aviso/config.yaml.sequential @@ -0,0 +1,12 @@ +listeners: + - event: mars + request: + class: od + expver: 1 + domain: g + stream: oper + step: [36, 72, 108, 144, 228] + triggers: + - type: command + working_dir: $HOME/data-retrieval/aviso + command: $HOME/data-retrieval/bin/get_ecmwf_aviso.sh --date ${request.date} --time ${request.time} --step ${request.step} > mars_${request.time}_${request.step}.out diff --git a/bin/convert_all.sh b/bin/convert_all.sh index 5d896c1..fd6c73c 100755 --- a/bin/convert_all.sh +++ b/bin/convert_all.sh @@ -31,7 +31,7 @@ fi # script should start at 06h/18h to look for 00 12h forecast export h_exit=`date --date="+6hours" +%H` -for FCSTEP in 036 072 108 144 228 +for FCSTEP in $FCSTEPS do # Set path, filenames and variables used later in the script @@ -42,7 +42,7 @@ do export init_date=${DATE}T${TIME} echo BASE: $BASE export time_units="hours since ${init_date}" - echo time_units: "hours since ${init_date}" + lockfile=grib/${DATASET}.${YMD}T${HH}.${FCSTEP}.ready echo `date` waiting for lockfile $lockfile @@ -58,6 +58,7 @@ do fi done rm $lockfile + # Convert grib to netCDF, set init time echo `date`: converting ${FCSTEP}h forecast . $BINDIR/convert.sh @@ -76,7 +77,6 @@ do fi if [ -f $pvfile ]; then mv $pvfile $MSSDIR - fi if [ -f $alfile ]; then mv $alfile $MSSDIR diff --git a/settings.default b/settings.default index 1e03e73..01d2c27 100644 --- a/settings.default +++ b/settings.default @@ -3,6 +3,10 @@ export PYTHON=python export CLEANUP=no +export CLEANUP_DAY=`date --date=-2day +%d` +export CLEANUP_MONTH=`date --date=-2day +%m` +export CLEANUP_YEAR=`date --date=-2day +%Y` + export MODEL_REDUCTION="-d lev,0,0 -d lev,16,28,4 -d lev,32,124,2" export DATASET=ecmwf From a3b8434c587553dbf2c192cbc9ab875708f0f09b Mon Sep 17 00:00:00 2001 From: Joern Ungermann Date: Tue, 25 Mar 2025 11:16:51 +0100 Subject: [PATCH 5/5] Fixed test cases. --- _test/mss/2021-01-29T00:00:00.an.sfc.nc | Bin 173085 -> 175922 bytes _test/test_cds.py | 1 + bin/convert.sh | 30 ++++++++++++++---------- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/_test/mss/2021-01-29T00:00:00.an.sfc.nc b/_test/mss/2021-01-29T00:00:00.an.sfc.nc index a0fb8be6848d14a7a3c0e19124bff354693ab799..5808da43b12ccdf74c4b7f24dc8437f0d093fbf9 100644 GIT binary patch delta 2661 zcmbtW3rtg27(Rb{p-?IEus|J;>kEnkg95rSACwk`6J=6?DX>!Ef}MT1K2!*b#7&1W z6L$>P$u{SF&CO}BW=*D>n^~NWxJ463=G1_TaWiVn;>=XnB^SUWNpmjjr znh=tc<=q-XCgdhc> zo%9vT0ewy#n$@y51#4C5+tm?xt}24*ZR`5Uvstl0hU1k{jOy6liyn=#5oSrr@l?Vh zT`3h%O9%x98|TvLi`ddtoWta@JDlul&LQYwb2G9sa&d2r7LX?@RxZ&G`6GK~f9^_k zqH-koV|xEewWn5<8jdHs=fGk7ZCAo`5fO+a?0OgxLY|r8g3#dKd)=PFlzRZ7o-Ai9 zVA%{cQ_`TkMJvIxct&R)hK2$Ont)ARam-(5iuUo{CP}_$bHn;h+>sg_ny^pIcMaa1 znu&`avz|<69C$Vi#6XB}if-JdO1x|EknaG#3Sb)l-Bd{Dqa@H%5>_DQEnEzm(`B;N zn;i8=w7xW~{T=-StL``AxUE-nho(;v6UzA>4gi$>&!9 z!{pq2X^ppE0+Xgtjm;zU1S6opL`i3~}d_Lj;T$z>f}=50)0|%Or|3{G_EY5BCk~ zyca6qu7rk)?_CBp{6}ht;orFo6N6Cy@hpwkTm^ZF{MSuT5QHWm#Nf6;C8Nu~6HmkM z)iuMRFpzuiY=So=Oo_H_m)N>3d~-Y4nUKxjYxq0ukS1vX+?S9cO;#WrnW$vu-W)(Y zw*{=B8-<{frU}K?OR0?a5AA2P@wV3ndsl8HA>>;+AUP5Zn-S~6ZTBi^$MGreK@_v; z;<874WF;)Y?Z55xrebIV^|Ll0p(zJGzaR3wjh$dj8mf+KUxJ$X!qu3X{Zw<#HryzPcBRsQm|i_C1$}?4?(TJx%Xd57;s>y}?3y3whU7SSn`r#x+>X z^xdx5LEH_Nc!|3u@o{2h1^LFLld3@|>}CG>*MQ~EK8B;TZ=#N$$`r+)7=Y#b@z0$} zb8`{tZYdd`bif4mN)#ar=oQ)?)83svTcDVo{S*qJ>;(#q(aMpwGS+izR@u!q*Zh6j z;{w4vGCKZKgD`VP61X@kop^2sr@VjAE^xe}eF{gSkHc&ed?bII-R(54H9Fmn22&kp zbh;d5h%=wEzyA-qrYGp6=W-7Vbgc`XqK-5T_0V74ur}eAS#IHsD>#$O*kEz5y>;4h zo?b5_U@+)#S2S*?HNTyv!F^O{OL{kG0jATrRGjO*gTA zb(6M4SD9u{t4s5nueG#jMm4Sov<{rcqaZa4DbQC}xHuxQRqwwyTx)^o`G(>m3V+s^ zk3vycY4E2g9kr@PK`t^pfuz7E>xab`6tOrXQkLPL1x;37=8J(@p#Pzyl^7P{{)p%y zXnG)Y=2%eO{E^5}4$U45sWmJb3C#&SG;1uVK~H079d;J)iJj=NC@_QLRb|qX2P8O4 LmB570nXv0$e74^O delta 1156 zcmY*XeN0RX4ID)O7#SP!ABpneefM=F-k0~zedqi9 zerISZ<@A-5UKe>W)!4VC36cjtNY&#Ucga{j2;wJ&w+wW{8 z6-l+pXAGJO6PPoRjfD@}X5yL>&43=rxp4 z8;eQ5?3TyV8i35CcsH9xrjEP+&lN4IaRG$%OrQpVeYPW%#Dp<&d6p&Uf^1J+T_x&O zb?iU@(17ZOAe?|mE*oj`0E>G)du?0Naoj1Xi4!aQb&yM}oq9^+?HZ55F(&9=nHiXJc8 zLav(2WlJU&=0vHG-WTCqa@|obwqyK0i8f^8q z2aZUs^t)UrqMrkdVav#gL1kJ1M~+&Kmghmdp3>n2E7*sBzhSCzd!IBK)ma8QI1cD} z^@kmLe+fzrBEKW#ef93w%CZYCZQ!{U>Q-|VKMh7$b8=7d4>jJ3S|Rl(>evU{R4-+G z58M;95$VJ;&_JbIkjDVjbJFX+s(wOgu7?skxz?XWukVF&Tvc`WTaJ?A?t}{lNLJ=v zgM$KItex-G);&f$yFtPa^{jiJ&UHg!0^3kBcsG+QWSecHF0I|Hc-If)OK%RGqro>I ztUIL+=_CiQ7h>n>b0NOUeb4EXLp^|TvSvjnksj)W4fu>B^ef+0>U?YkePbv8Xx