#!/bin/bash #set -vx #============================================================================= # F. Hourdin : 2022/05/03 # # Objet : # ----- # Script préparant les programes pour rejouer (replay) une paramétrisation # ou un morceau de paramétriation. # # # Applicabilité : # --------------- # Est fait pour rejouer des paramétisation de LMDZ organisées comme # des calcul sur des successions de colonnes indépendantes (boucle # interne en $klon) de $klev niveaux verticaux. # Demande que la paramétrisation répondent aux règles de codage suivantes : # 1) Les routines de calcul et les routines ou fonctions qu'elles appellent # n'ont que deux interfaces : A) les arguments d'entrée # B) le use d'un unique module # Entre autre, les dimensions de la paramétrisation sont passés # en argument, ce qui permet une allocation dynamique implicite # de toutes les variables. # 2) le module d'initialisation doit lui même être initialisé par # une unique routine pilotée entièrement par ses arguments. # 3) Toutes les variables d'interface doivent être déclarées intent in # out, ou inout # On appelera la paramétriation "param" et l'intialisation "param_ini" # mais les noms de fichiers sont en argument dans le script. # # # Principe : # ---------- # Dans une première simulation, on écrit dans un fichier binaire toutes # les variables "intent in/inout" de la routine d'initialisation et de la routine # de calcul. # # # En pratique : # ------------- # # Le script a comme argument le nom de la routine à traiter, nommée $param # Des correpspondances en déduisent : # param_ini=wake_ini # nom de la subroutine d'intialisation # inimod= # nom du module contenant $param_ini # klon=klon ; klev=klev # nom des dimensions horiz; vert utilisée # # Le fichier ${paramfile} contenant $param est detecté automatiquement # Le script crée 5 fichiers # * dump_param1.h : écriture de l'interface "in" de param (dump_param.bin fort.82) # * dump_param2.h : écriture des sorties de la routines dans phys.nc # * call_param_replay : sous programme d'appelle en boucle à la param # * dump_ini_module.${f90} : écriture de l'interface "in" de param_ini # * call_ini_replay.${f90} : lecture de l'interface "in" de param_ini # dans ${param_ini}_mod.bin. # Par ailleurs, un programme replay1d a été ajouté dans phylmd/dyn1d # qui appelle call_param_replay # Le script ajoute par ailleurs quelques lignes dans ${paramfile} et # ${param_ini}.${f90} # replay_clean.sh élimine toutes les lignes ajoutées # # # A travailler : # -------------- # * détecter automatiquement le fichier contenant l'intialisation # * détecter automatiquement les dimensions # * avoir un moyen de vérifier si les variables intent in et out sont # bien les bonnes. # * Initialisation plus simple de la routine getin_p # * Des choix sur la facon de controler l'initialisation et le pb des getin # #============================================================================= f90=f90 #----------------------------------------------------------------------------- # Reading rguments #----------------------------------------------------------------------------- nconly=false where=-before_return lonlat= if [ $# = 0 ] ; then $0 -h ; exit ; fi while (($# > 0)) do case $1 in -h|--help) cat <<........fin $0 [-nconly] [-ncvars var1,var2,...] [-pre prefix] [location_in_the_code] [-lonlat lon,lat] routine_name The prefix will be put on each variable stored in the nc file to avoid duplicate variables names If -ncvars is not specified, all the variables are output The prefix is used to prevent having twice the same name if the same variable is output at two locations in the code. The -nconly option can be used to equip other routines with nc output only location_in_the_code can be : -before_return (the default valuer) -after_declarations -before_line "line in code" -after_line "line in code" -before_call "param_name" (TO BE DONE) -after_call "param_name" (TO BE DONE) -lonlat longitude,latitude ........fin exit ;; -nconly) nconly=true ; shift ;; -before_line|-after_line|-before_call|-after_call) where="$1 $2" ; shift ; shift ;; -before_return|-after_declarations) where=$1 ; shift ;; -pre) prefix=$2 ; shift ; shift ;; -ncvars) ncvars="`echo $2 | sed -e 's/,/ /g'`" ; shift ; shift ;; -lonlat) lonlat=$2 ; shift ; shift ;; *) if (( $# > 1 )) ; then $0 -h ; exit ; fi ; param=`basename $1 .${f90}` ; shift esac done if [ "$param" = "" ] ; then $0 -h ; exit ; fi case $param in vdif_k|vdif_cd|vdif|my_25|vdif_dq) param_ini=vdif_ini ; inimod=simple_vdif_ini ; klon=ngrid ; klev=nlay ;; thermcell_plume_6A|thermcell_env|thermcell_height|thermcell_dry|\ thermcell_closure|thermcell_height|thermcell_dq|thermcell_flux2|thermcell_down| \ thermcell_updown_dq|thermcell_dtke) \ param_ini=thermcell_ini ; inimod=lmdz_thermcell_ini ; klon=ngrid ; klev=nlay ;; wake|pkupper|wake_popdyn_1|wake_popdyn_2|vdif_kcay|ustarhb) param_ini=wake_ini ; inimod=lmdz_wake_ini ; klon=klon ; klev=klev ;; surf_wind) param_ini=surf_wind_ini ; inimod=lmdz_surf_wind_ini ; klon=klon ; klev=nsrfwnd ;; ratqs_main|ratqs_inter|ratqs_oro|ratqs_hetero|ratqs_tke) param_ini=ratqs_ini ; inimod=lmdz_ratqs_ini ; klon=klon ; klev=klev ;; lscp|fisrtilp) param_ini=lscp_ini ; inimod=lmdz_lscp_ini ; klon=klon ; klev=klev ;; *) echo Cas non prevu ; exit esac replay_comment="replay automatic include" paraminc1=dump_replay_${param}_head.h paraminc2=dump_replay_${param}_nc_${prefix}.h iniinc=dump_replay_ini.h paramfile=`grep -i "subro.* ${param}[\ (]" *.${f90} | sed -e 's/ //g' | grep "${param}(" | cut -d: -f1` echo =================================================================================== echo Equiping $param contained in file $paramfile if [ `echo ${paramfile} | wc -w` != 1 ] ; then echo file $paramfile multiple ; $0 -h ; exit ; fi #----------------------------------------------------------------------------- # Transformer l'entete d'une subroutine en un call #----------------------------------------------------------------------------- function get_subroutine_arg(){ tmp=tmp_get_subroutine_arg cat $2 | tr '[A-Z]' '[a-z]' > ${tmp} line1=`sed -n -e '/subrou.*'\`echo $1 | tr '[A-Z]' '[a-z]'\`'.*(/=' ${tmp} | head -1 ` line2=`tail -n +$line1 ${tmp} | sed -n -e '/)/=' | head -1` tail -n +$line1 ${tmp} | sed -n -e 1,${line2}p } #----------------------------------------------------------------------------- function var_get_dims(){ #----------------------------------------------------------------------------- local var=$1 local line=$( grep dimension input | grep '::.*'$var | grep -w $var | sed -e 's/ //g' ) local pattern='dimension\s*\(([a-z0-9,+]*)\)' if [[ $line =~ $pattern ]] ; then echo ${BASH_REMATCH[1]} | sed -e 's/,/ /g' fi } #----------------------------------------------------------------------------- function var_dims(){ #----------------------------------------------------------------------------- local var=$1 local dims=$(var_get_dims $var) if [[ "$dims" != "" ]] ; then local dims_= for d in $dims ; do if [[ $d = $klon ]] ; then dims_="${dims_} 1:$klon" else dims_="$dims_ :" fi done echo \(${dims_}\) | sed -e 's/( /(/' -e 's/ /,/g' fi } #----------------------------------------------------------------------------- function var_recl(){ #----------------------------------------------------------------------------- local var=$1 local dims=$( var_get_dims $var ) if [[ "$dims" != "" ]] ; then local dims_= ; for i in $dims ; do dims_="$dims_ ($i)" ; done echo $dims_ | sed -e 's/ /*/' else echo 1 fi } #----------------------------------------------------------------------------- function dump_param_open(){ #----------------------------------------------------------------------------- # Opening an direct access file with one record per time-step # Each record has the size and contains the arguments in and inout of the # routine #----------------------------------------------------------------------------- inout=$1 ; shift case $inout in out) fort=81 ;; in) fort=82 esac echo '! <<< dump_param_open' echo 'print*,"NOUVEAU REPLAY"' for var in $* ; do #echo 'rec_length_replay=rec_length_replay+kind('$var')*size(['$var'])' echo 'rec_length_replay=rec_length_replay+kind('$var')*'$( var_recl $var ) done cat <<....eod open(${fort},file='dump_param_${inout}.bin',form='unformatted',access='direct',recl=rec_length_replay) ! $replay_comment ....eod echo '! dump_param_open >>> ' } #----------------------------------------------------------------------------- function extract_subroutine(){ #----------------------------------------------------------------------------- # $1 nom de la subroutine # $2 nom du fichier # input <- routine under treatment with small caps only tmp=tmp_extract_subroutine ( cpp $2 2>/dev/null ) | tr '[A-Z]' '[a-z]' > ${tmp} name_min=`echo $1 | tr '[A-Z]' '[a-z]'` line1=`sed -n -e "/subrou.*${name_min}.*(/=" ${tmp} | head -1 ` tail -n +$line1 ${tmp} > ${tmp}2 line2=`sed -n -e "/[Rr][Ee][Tt][Uu][Rr][Nn]/=" ${tmp}2 | head -1` head -$line2 ${tmp}2 > input \rm -f ${tmp} ${tmp}2 } #----------------------------------------------------------------------------- function echo_use_module(){ #----------------------------------------------------------------------------- # Regle tacite : l'instruction MODULE commence à la premiere colonne. # Existe-t-i une facon de la faire tomber ? # En enlevant tous les blanc dans le input sans doute ... param_=$1 ; shift paramfile_=$1 if [ ! -f $paramfile_ ] ; then echo plantage which_module ; exit 1 ; fi module=`grep '^[mM][oO][dD][uU][lL][eE] .*[a-Z]' $paramfile_ | head -1 | awk ' { print $2 } '` if [ "$module" != "" ] ; then echo USE $module, ONLY : $param_ fi } #----------------------------------------------------------------------------- function include_line(){ #----------------------------------------------------------------------------- # Including param_dump*.h in the parameterization #set -vx tmp=tmp_include_line line_to_be_included=$1 ; shift param_=$1 ; shift paramfile_=$1 ; shift name_min=`echo $param_ | tr [A-Z] [a-z]` line_subroutine=`cat $paramfile_ | tr '[A-Z]' '[a-z]' | sed -n -e "/subrou.*${name_min}.*(/=" | head -1 ` tail -n +$line_subroutine $paramfile_ > ${tmp} # file starting at the subroutine instruction line_return=`sed -n -e "/[Rr][Ee][Tt][Uu][Rr][Nn]/=" ${tmp} | head -1` head -$line_return ${tmp} > ${tmp}2 # file containing the routine sed -e 's/\!.*$//' ${tmp}2 > ${tmp}3 cpp ${tmp}2 > ${tmp}3 2>/dev/null cat ${tmp}3 | tr '[A-Z]' '[a-z]' > ${tmp}2_cpp # same after cpp filtering last_line_declaration2="`sed -n -e 's/\!.*$//' -e 's/^/ /' -e '/[\ ,]real[\ ,]/p' -e '/[\ ,]integer[\ ,]/p' -e '/[\ ,]logical[\ ,]/p' -e '/[\ ,]character[\ ,]/p' ${tmp}2_cpp | tail -1 | sed -e 's/ / /g' -e 's/ /.*/g'`" line_last_declaration=`cat ${tmp}2 | tr '[A-Z]' '[a-z]' | sed -n -e "/$last_line_declaration2/="` echo OK0 if [ "`grep -i 'end.*module' $paramfile_`" = "" ] ; then echo aka ; line_end_module=`wc -l $paramfile_ | awk ' { print $1 } '` ; else echo akb ; line_end_module=`sed -n -e '/[eE][nN][dD] .*[mM][oO][dD][uU][lL][eE]/=' $paramfile_` ; fi echo OK1 echo $line_end_module if [ "$line_last_declaration" = "" ] ; then echo line_last_declaration $line_last_declaration line $last_line_declaration2 ; exit ; fi if (( $# > 0 )) ; then wtype=`echo $1 | awk ' { print $1 } '` case $wtype in -after_declarations) targeted_line=$line_last_declaration ;; -before_return) targeted_line=$line_return ;; -before_end_module) targeted_line=$line_end_module ;; -before_line|-after_line) linestr=`echo $1 | sed -e "s/$wtype //"` ; targeted_line=`sed -n -e "/$linestr/=" ${tmp}2` ;; *) echo Cas non prevu 0 where=$where in include_file esac case $wtype in -after_declarations|-after_line) line0=$(( $line_subroutine - 1 )) ;; -before_return|-before_line) line0=$(( $line_subroutine - 2 )) ;; -before_end_module) line0=-1 ;; *) echo Cas non prevu 1 where=$where in include_file esac echo Including line $line0 + $targeted_line in $paramfile_ the line : $line_to_be_included linebefore_include=$(( $line0 + $targeted_line )) sed -i'' -e $linebefore_include"s/$/\n $line_to_be_included \! $replay_comment/" $paramfile_ fi } #----------------------------------------------------------------------------- function block_Replay0() { #----------------------------------------------------------------------------- condition=$1 ; shift suf1=$1 ; shift suf2=$1 ; shift if [ $# -gt 0 ] ; then vars=$* echo ' if ('$condition') then' for var in $vars ; do echo ' '$var$suf1=$var$suf2 ; done echo ' endif' fi } #----------------------------------------------------------------------------- function iotd_calls(){ #----------------------------------------------------------------------------- klev_=$1 ; shift pre=$1 ; shift if (( $# >> 0 )) ; then vars=$* echo "if ( abs(gr_index_)>0 ) then" for var in $vars ; do local vardim=$( var_dims $var ) echo "call iotd_ecrit_seq('"$pre$var"',$klev_,'"$pre$var in $param"',' ',"$var$vardim")" done echo endif fi } #----------------------------------------------------------------------------- function b_among_a() { #----------------------------------------------------------------------------- a="$1" b="$2" o="" for v in $a ; do for vv in $b ; do if [ "$v" = "$vv" ] ; then o="$o $v" ; fi done done echo $o } #----------------------------------------------------------------------------- # On nettoye les inclusions précédente dans les fichiers .${f90} #----------------------------------------------------------------------------- if [ $nconly = false ] ; then for file in `grep "$replay_comment" *.${f90} 2> /dev/null | cut -d: -f1` ; do sed -i"" -e "/$replay_comment/d" $file done #line=`sed -n -e "/CALL "$param_ini"/=" physiq_mod.F90 | head -1` line=`sed -n -e "/CALL wake_ini/=" physiq_mod.F90 | head -1` if [[ "$lonlat" = "" ]] ; then if [[ $klev == klev || $klev == nlay ]] ; then klev_=klev else klev_=$klev fi pattern="CALL iophys_ini(pdtphys,$klev_)" else pattern='call iotd_ini("phys.nc",1,1,klev,'$lonlat',presnivs,1,1,1,0.,pdtphys,calend)' fi sed -i"" -e "${line}s/^/ $pattern ! $replay_comment ! $replay_comment\n/" physiq_mod.F90 fi #----------------------------------------------------------------------------- # Analysis of the variables to be stored and there nature #----------------------------------------------------------------------------- extract_subroutine $param $paramfile # -> input file #var_get_dims ztv #var_dims ztv #exit # var_recl ztv # var_dims pplev # var_recl pplev # var_dims ngrid # var_recl ngrid # exit varin0=`grep inten.*.in input | sed -e 's/\!.*$//' | cut -d: -f3 | sed -e 's/,/ /g'` varinout0=`grep inten.*.inout input | sed -e 's/\!.*$//' | cut -d: -f3 | sed -e 's/,/ /g'` echo varin0 $varin0 varin_rec= ; for v in $varin0 ; do varin_rec="$varin_rec $v$( var_dims $v)" ; done varin=`echo $varin_rec | sed -e 's/ /,/g' -e "s/,,,/,/g" -e "s/,,/,/g"` #echo $varin $varin output=full # Attention, l'option full ne marche pas a cause de tableaux locaux undef nvar0D=0 ; nvar1D=0 ; nvar2D=0 case $output in light) search_str='real.*intent' ;; full) search_str='real' esac var_1D_inout=`grep -i "$search_str" input | grep intent.*inout | grep $klon | sed -e 's/!.*$//' -e /$klev/d | cut -d: -f3 | sed -e 's/,/ /g'` var_2D_inout=`grep -i "$search_str" input | grep intent.*inout | grep $klon | grep $klev | cut -d: -f3 | sed -e 's/!.*$//' -e 's/,/ /g'` var_1D_noarg=`grep -i "$search_str" input | sed -e /intent/d | grep $klon | sed -e 's/!.*$//' -e /$klev/d | cut -d: -f3 | sed -e 's/,/ /g'` var_2D_noarg=`grep -i "$search_str" input | sed -e /intent/d | grep $klon | grep $klev | cut -d: -f3 | sed -e 's/!.*$//' -e 's/,/ /g'` var_1D=`grep -i "$search_str" input | grep $klon | sed -e 's/!.*$//' -e /$klev/d | cut -d: -f3 | sed -e 's/,/ /g'` var_2D=`grep -i "$search_str" input | grep $klon | grep $klev | cut -d: -f3 | sed -e 's/!.*$//' -e 's/,/ /g'` if [ "$ncvars" != "" ] ; then var_1D=`b_among_a "$var_1D" "$ncvars"` var_2D=`b_among_a "$var_2D" "$ncvars"` fi echo Variables in and inout : $varin0 echo 1D variables "(all)" : $var_1D echo 2D variables "(all)" : $var_2D echo 1D variables "intent(inout)" : $var_1D_inout echo 2D variables "intent(inout)" : $var_2D_inout echo local 1D variables : $var_1D_noarg echo local 2D variables : $var_2D_noarg #----------------------------------------------------------------------------- # Ecriture de la routine d'appel a la parametrisation en mode replay #----------------------------------------------------------------------------- if [ $nconly = false ] ; then cat > call_param_replay.${f90} <> call_param_replay.${f90} # duplicating declaration for inout variables for var in $varinout0 ; do sed -e 's/::/ :: /' -e 's/,/ , /g' -e 's/$/ /' input | grep 'intent.*inout.*::.* '$var' ' | sed -e 's/ //g' -e 's/,intent(.*[nt])//' | sed -e 's/::.*$/, allocatable, save :: '$var'_Replay0/' | sed -e 's/'$klon'/:/' -e 's/'${klev}'+1/:/' -e 's/'${klev}'/:/' >> call_param_replay.${f90} done # Initalisation, shared with dump_param1.sh dump_param_open "in" $varin0 >> call_param_replay.${f90} for var in $varinout0 ; do sed -e 's/::/ :: /' -e 's/,/ , /g' -e 's/$/ /' input | grep 'intent.*inout.*::.* '$var' ' | sed -e 's/intent(.*t)//' -e 's/^.*(/allocate('$var'_Replay0(/' -e 's/).*$/))/' >> call_param_replay.${f90} done cat >> call_param_replay.${f90} <=0) then irec=replay_irec0+it-1 else irec=-replay_irec0 endif print*,'Time step',it,', reading record',irec,'in dump_param.bin' read(82,rec=irec,iostat=ifin) $varin if (.NOT. ifin == 0 ) stop "Fin du fichier fort.82" eod block_Replay0 "it == 1" _Replay0 "" $varinout0 >> call_param_replay.${f90} block_Replay0 "replay_irec0 < 0" "" _Replay0 $varinout0 >> call_param_replay.${f90} get_subroutine_arg $param $paramfile | sed -e 's/subroutine/ call/' >> call_param_replay.${f90} block_Replay0 "replay_irec0 < 0" _Replay0 "" $varinout0 >> call_param_replay.${f90} cat >> call_param_replay.${f90} < $paraminc1 <> $paraminc1 ; fi echo "if ($klon==1) gr_index_=1" >> $paraminc1 echo "if (abs(gr_index_)>0) then" >> $paraminc1 echo "if (first_replay) then" >> $paraminc1 dump_param_open out $varin0 >> $paraminc1 cat>> $paraminc1 <> $paraminc1 iotd_calls $klev in_${prefix} $var_2D_inout >> $paraminc1 fi # nconly = false if [ "`grep $paraminc1 $paramfile`" = "" ] ; then # Not changing $paraminc1 if it is already included in the file # To allow adding new nc outputs in a routine equiped for replay for var in $var_1D_noarg $var_2D_noarg ; do echo $var=0. >> $paraminc1 ; done include_line "include \"$paraminc1\"" $param $paramfile -after_declarations fi #----------------------------------------------------------------------------- # Creating file dump_${param}_nc_${prefix}.h #----------------------------------------------------------------------------- \rm $paraminc2 iotd_calls 1 "${prefix}" $var_1D >> $paraminc2 iotd_calls $klev "${prefix}" $var_2D >> $paraminc2 include_line "include \"$paraminc2\"" $param $paramfile "$where" if [[ "$lonlat" != "" ]] ; then for file in $paraminc1 $paraminc2 ; do sed -i -e "s/1:$klon/gr_index_/g" -e '/rec_l/s/\*('$klon')//g' $file ; done sed -i -e "/(81/s/,$klon/,1/" $paraminc1 fi #---------------------------------------------------------------------------- # dump_ini_module gere l'ecriture des variables d'interface de l'intialisation # du module en mode replay #----------------------------------------------------------------------------- if [ $nconly = false -a $param_ini != None ] ; then varinmod=`grep -i 'intent.in' $inimod.${f90} | sed -e 's/\!.*$//' | cut -d: -f3 | sed -e 's/,/ /g'` varinmodv=`echo $varinmod | sed -e 's/ /,/g'` cat > $iniinc <<...eod............................................... open(90,file='$inimod.bin',form='unformatted') write(90) $varinmodv close(90) ...eod............................................... for var_ in $varinmod ; do echo "print*,'Interface $param_ini $var_',$var_" >> $iniinc ; done include_line "include \"$iniinc\"" $param_ini $inimod.${f90} -before_return fi # nconly = false #----------------------------------------------------------------------------- # call_ini_replay.${f90} gere l'initialisation du module en mode replay #----------------------------------------------------------------------------- if [ $nconly = false -a $param_ini != None ] ; then cat > call_ini_replay.${f90} <<....eod............................................ subroutine call_ini_replay use $inimod IMPLICIT NONE ....eod............................................ grep -i intent.in $inimod.${f90} | tr '[A-Z]' '[a-z]' | sed -e 's/,.*intent.i.*)//' >> call_ini_replay.${f90} cat >> call_ini_replay.${f90} <<....eod........................................... open(90,file='$inimod.bin',form='unformatted') read(90) $varinmodv close(90) ....eod........................................... #get_subroutine_arg $param_ini $inimod.${f90} ; exit get_subroutine_arg $param_ini $inimod.${f90} | sed -e 's/subroutine/call/' >> call_ini_replay.${f90} cat >> call_ini_replay.${f90} <<....eod........................................... return end ....eod........................................... fi # nconly = false