#!/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 :
#   paramini=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.F90 : sous programme d'appelle en boucle à la param
#  * dump_ini_module.F90   : écriture de l'interface "in" de param_ini
#  * get_ini_module.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
#
#=============================================================================

param=`basename $1 .F90`
case $param in
   thermcell_main|thermcell_plume_6A|thermcell_env|thermcell_height|thermcell_dry|\
      thermcell_closure|thermcell_height|thermcell_dq|thermcell_flux2|thermcell_down) \
      paramini=thermcell_ini ; inimod=thermcell_ini_mod ; klon=ngrid ; klev=nlay ;;
   wake|wake_popdyn_2) paramini=wake_ini ; inimod=wake_ini_mod ; klon=klon ; klev=klev ;;
   *) echo Cas non prevu ; exit
esac

replay_comment="replay automatic include"
paraminc1=dump_param1.h
paraminc2=dump_param2.h
iniinc=dump_ini.h

#-----------------------------------------------------------------------------
# Transformer l'entete d'une subroutine en un call
#-----------------------------------------------------------------------------

function 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 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'
for var in $* ; do echo 'rec_length=rec_length+kind('$var')*size(['$var'])' ; done
cat <<eod
open(${fort},file='dump_param_${inout}.bin',form='unformatted',access='direct',recl=rec_length)  ! $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
   cpp $2 | 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 > tmp2
   line2=`sed -n -e "/[Rr][Ee][Tt][Uu][Rr][Nn]/=" tmp2 | head -1`
   head -$line2 tmp2  > input
   \rm -f tmp tmp2
}

#-----------------------------------------------------------------------------
function include_line(){
#-----------------------------------------------------------------------------
   # Including param_dump*.h in the parameterization
   line_to_be_included=$1 
   param_=$2
   paramfile_=$3
   where=$4
   name_min=`echo $param_ | tr [A-Z] [a-z]`
   echo $name_min ...
   line_subroutine=`cat $paramfile_ | tr '[A-Z]' '[a-z]' | sed -n -e "/subrou.*${name_min}.*(/=" | head -1 `
   echo LINE $line_subroutine
   tail -n +$line_subroutine $paramfile_ > tmp
   line_return=`sed -n -e "/[Rr][Ee][Tt][Uu][Rr][Nn]/=" tmp | head -1`
   line_declarations=`head -$line_return tmp | tr '[A-Z]' '[a-z]' | sed -n -e 's/\!.*$//' -e "/[\ ,]real[\ ,]/=" -e "/[\ ,]integer[\ ,]/=" -e "/[\ ,]logical[\ ,]/=" -e "/[\ ,]character[\ ,]/=" | tail -1` 
   echo lignes $line_subroutine $line_return $line_declarations
   case $where in
      after_declarations) linebefore_include=$(( $(( $line_subroutine + $line_declarations )) -1 )) ;;
      before_return)      linebefore_include=$(( $(( $line_subroutine + $line_return )) -2 )) ;;
      *)                  echo Cas non prevu where=$where in inclide_file
   esac
   # including lines in revers order
   sed -i'' -e $linebefore_include"s/$/\n $line_to_be_included \!  $replay_comment/" $paramfile_ 
}

#-----------------------------------------------------------------------------
function get_param_file(){
#-----------------------------------------------------------------------------
  p=$1
  grep -i "subro.* $p[\ (]" *.F90 | sed -e 's/ //g' | grep "$p(" | cut -d: -f1
}

#-----------------------------------------------------------------------------
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
}

#-----------------------------------------------------------------------------
# On nettoye les inclusions précédente dans les fichiers .F90
#-----------------------------------------------------------------------------

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 wake_ini/=" physiq_mod.F90 | head -1`
sed -i"" -e "${line}s/$/\n   CALL iophys_ini(pdtphys) ! $replay_comment  ! $replay_comment/" physiq_mod.F90

#-----------------------------------------------------------------------------
# Modifying the parameterization routine
#-----------------------------------------------------------------------------

paramfile=`get_param_file $param`
echo La parametrisation $param est contenue dans le fichier $paramfile
if [ `echo ${paramfile} | wc -w` != 1 ] ; then echo file $paramfile multiple  exit ; fi
extract_subroutine $param $paramfile

#-----------------------------------------------------------------------------
# Liste des variables d'intent in pour stokage
#-----------------------------------------------------------------------------

varin0=`grep inten.*.in input | sed -e 's/\!.*$//' | cut -d: -f3 | sed -e 's/,/ /g'`
varinout0=`grep inten.*.inout input | sed -e '/\!.*$/d' | cut -d: -f3 | sed -e 's/,/ /g'`
varin=`echo $varin0 | sed -e 's/ /,/g' -e "s/,,,/,/g" -e "s/,,/,/g"`
echo varin $varin0 
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_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'`
echo varin  : $varin
echo var_1D_noarg : $var_1D_noarg
echo var_2D_noarg : $var_2D_noarg
echo var_1D : $var_1D
echo var_2D : $var_2D

#-----------------------------------------------------------------------------
# Ecriture de la routine d'appel a la parametrisation en mode replay
#-----------------------------------------------------------------------------

cat > call_param_replay.F90 <<eod
subroutine call_param_replay($klon,$klev)
USE IOIPSL, ONLY : getin
IMPLICIT NONE
integer :: ifin,irec,it,rec_length=0,replay_irec0=1,replay_nt=1000
eod
grep 'intent.*::' input | sed -e 's/ //g' -e 's/,intent(.*[nt])//'>> 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'/:/' >> 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 <<eod
call getin('replay_nt',replay_nt)
call getin('replay_irec0',replay_irec0)
do it=1,replay_nt
   if (replay_irec0>=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 <<eod
enddo
return
end
eod


#-----------------------------------------------------------------------------
# Creation d'un .h d'ecriture de toutes les variables intent in & out
#-----------------------------------------------------------------------------

\rm $paraminc1
cat> $paraminc1 <<eod
logical, save :: first=.true.
integer, save :: rec_length=0,irec=0
if (first) then
eod
dump_param_open out $varin0 >> $paraminc1
cat>> $paraminc1 <<eod
first=.false.
endif
irec=irec+1
write(81,rec=irec) $varin
eod
for var in $var_1D_noarg $var_2D_noarg ; do echo $var=0. >> $paraminc1 ; done
include_line "include \"$paraminc1\"" $param $paramfile after_declarations

\rm $paraminc2
for var in $var_1D ; do
echo "call iotd_ecrit_seq('"$var"',1,'"$var"',' ',"$var")" >> $paraminc2
done
for var in $var_2D ; do
echo "call iotd_ecrit_seq('"$var"',"$klev",'"$var"',' ',"$var")" >> $paraminc2
done
include_line "include \"$paraminc2\"" $param $paramfile before_return
  
#-----------------------------------------------------------------------------
# dump_ini_module gere l'ecriture des variables d'interface de l'intialisation
# du module en mode replay
#-----------------------------------------------------------------------------

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
include_line "include \"$iniinc\"" $paramini $inimod.F90  before_return


#-----------------------------------------------------------------------------
# get_ini_module gere l'initialisation du module en mode replay
#-----------------------------------------------------------------------------

cat > get_ini_module.F90 <<eod
subroutine get_ini_module
use $inimod
IMPLICIT NONE
eod
grep -i intent.in $inimod.F90  |  tr '[A-Z]' '[a-z]' | sed -e 's/,.*intent.i.*)//' >> get_ini_module.F90
cat >> get_ini_module.F90 <<eod
open(90,file='$inimod.bin',form='unformatted')
read(90) $varinmodv
close(90)
eod
#get_subroutine_arg $paramini $inimod.F90 ; exit
get_subroutine_arg $paramini $inimod.F90 | sed -e 's/subroutine/call/' >> get_ini_module.F90
cat >> get_ini_module.F90 <<eod
return
end
eod

#-----------------------------------------------------------------------------
# Inclusion de l'ecriture de l'interface de l'initialisation
#-----------------------------------------------------------------------------


#\rm -f tmp input
